NIS2 Compliance

AuditChain's cryptographic hash chain mode (HasAuditTrail) provides tamper-evident logging, automated chain verification, and real-time alerting — capabilities that directly address cybersecurity audit requirements. These features align with the NIS2 Directive (EU 2022/2555), which requires essential and important entities to implement risk analysis, incident handling, and supply chain security measures under Article 21.

Article 21 Requirements and AuditChain

Incident Handling (Article 21(2)(b))

NIS2 requires organizations to detect, respond to, and report security incidents. AuditChain helps by providing:

  • Immutable audit trail — Every change to your Eloquent models is recorded in a tamper-evident log. If a breach occurs, you have a reliable record of what was accessed or modified and when.
  • personal_data_accessed tracking — Each audit log entry records which personal data fields were involved, making it straightforward to assess the scope of a breach as required by GDPR Article 33 (which NIS2 references).
  • Read event capture — Enable log_reads to track who accessed sensitive records, not just who modified them.
// config/audit-chain.php
'events' => [
    'log_reads' => true,
],

Risk Analysis and Security Policies (Article 21(2)(a))

Chain verification provides continuous assurance that your audit data has not been tampered with:

# Verify the entire chain
php artisan audit:verify

# Verify a specific model type
php artisan audit:verify --type="App\Models\User"

# Verify a specific record
php artisan audit:verify --type="App\Models\User" --id=42

Automated Monitoring

Schedule regular chain verification and configure notifications so your team is alerted immediately if tampering is detected:

// routes/console.php
Schedule::command('audit:verify --notify')->hourly();

When the --notify flag is used and verification fails, AuditChain sends alerts through the configured channels:

// config/audit-chain.php
'notifications' => [
    'channels' => ['mail', 'webhook'],
    'mail_to' => [env('AUDIT_ALERT_EMAIL', 'security@example.com')],
    'webhooks' => [
        env('AUDIT_ALERT_WEBHOOK_1'), // Slack, Teams, Discord, or custom
    ],
],

Webhook payloads include both text and content keys for cross-platform compatibility with Slack, Microsoft Teams, Discord, and custom endpoints.

Supply Chain Security (Article 21(2)(d))

AuditChain supports a separate database connection for audit logs, isolating them from the main application database. This limits the blast radius if the application database is compromised:

// config/audit-chain.php
'connection' => 'audit', // dedicated DB connection

For maximum immutability, use a database user with INSERT and SELECT only on the audit table. Eloquent guards prevent modification at the application layer, but database-level restrictions ensure immutability even if the application itself is compromised.

Tamper Detection

The SHA-256 hash chain is AuditChain's core defense against tampering. Each audit log entry contains:

  • A hash computed from the audit data (event, values, user, IP, timestamp, and the previous hash)
  • A prev_hash linking it to the previous entry in the chain

Modifying, inserting, or deleting any entry in the chain breaks the link and is detected during verification. This provides the kind of tamper-evidence that NIS2 Article 21 expects from cybersecurity logging systems.

You can also verify the chain programmatically:

use GrayMatter\AuditChain\Services\AuditChainService;

$result = app(AuditChainService::class)->verifyChain();
// ['valid' => true, 'checked' => 1500, 'errors' => []]

if (! $result['valid']) {
    // Handle chain compromise -- alert security team, lock down system
}

For organizations subject to NIS2, we recommend the following configuration:

// config/audit-chain.php
return [
    // Isolate audit data on a separate connection
    'connection' => 'audit',

    // Use queue to avoid blocking request lifecycle
    'queue' => [
        'enabled' => true,
        'connection' => null,
        'queue' => 'audit',
    ],

    // Enable read tracking for sensitive models
    'events' => [
        'log_reads' => true,
    ],

    // Retain logs for regulatory minimum (adjust per your jurisdiction)
    'retention' => [
        'days' => 365,
    ],

    // Alert on chain compromise
    'notifications' => [
        'channels' => ['mail', 'webhook'],
        'mail_to' => ['security@example.com', 'dpo@example.com'],
        'webhooks' => [
            env('AUDIT_ALERT_WEBHOOK_1'),
        ],
    ],
];

And in your scheduler:

// routes/console.php
Schedule::command('audit:verify --notify')->hourly();
Schedule::command('audit:prune --days=365')->daily();

Important: Set AUDIT_CHAIN_SEED to a random, secret value in your .env file. The chain seed is used to compute the genesis hash. A predictable seed weakens the tamper-evidence guarantees that NIS2 compliance depends on.