# Log centralization and integrity verification is implemented

- **Pattern:** `ab-001192` (`ecommerce-pci.monitoring-compliance.log-centralization`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001192
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001192)

## Why it matters

PCI-DSS 4.0 Req 10.3 requires that audit logs are protected from destruction and unauthorized modifications; Req 10.4 mandates that log content is reviewed at least once daily. Logs stored only on the same server they monitor can be deleted by an attacker to cover their tracks. CWE-778 (Insufficient Logging) combined with writable log storage means a compromised host can silence its own forensic record. NIST AU-9 requires protection of audit information. S3 Object Lock (WORM) combined with KMS encryption satisfies Req 10.3 — an attacker with application-level access cannot delete an Object Lock-protected log archive.

## Severity rationale

Medium because logs stored in mutable, non-centralized locations can be deleted by a compromised component, eliminating forensic evidence and violating PCI-DSS 4.0 Req 10.3's tamper-evidence requirement.

## Remediation

Forward logs from at least two sources (application, database) to a centralized log group, then export to an S3 bucket with Object Lock enabled so logs are write-once and cannot be deleted. Encrypt the archive with a KMS key that is managed separately from the CDE application credentials.

```hcl
resource "aws_s3_bucket_object_lock_configuration" "log_archive" {
  bucket = aws_s3_bucket.log_archive.id
  rule {
    default_retention {
      mode = "GOVERNANCE"
      days = 365
    }
  }
}
```

Add a CloudWatch Subscription Filter to stream CDE log groups into the central archive log group. Document the centralization topology in `docs/pci-compliance.md` — a QSA will ask to see how Req 10.3 tamper protection is implemented.

## Detection

- **ID:** `log-centralization`
- **Severity:** `medium`
- **What to look for:** Count all log sources in the project (application logs, database logs, infrastructure logs, access logs). For each source, check whether logs are forwarded to a central location. Count the number of integrity verification mechanisms: HMAC/hash signing, immutable storage (S3 Object Lock, WORM), checksums, or audit trail services. Enumerate all log stream/subscription/forwarding configurations.
- **Pass criteria:** At least 2 log sources are forwarded to a central location (CloudWatch, ELK, Splunk, Datadog, or equivalent). At least 1 log integrity verification mechanism exists (cryptographic signing, immutable storage with object lock, or checksum validation). Report: "X log sources centralized, Y integrity verification mechanisms."
- **Fail criteria:** Logs stored in isolated locations without centralization (each service logs locally), or centralized but no integrity verification (0 signing, hashing, or immutable storage configs).
- **Skip (N/A) when:** Payment processing fully delegated to third-party (no local CDE logs generated, no infrastructure logging needed).
- **Detail on fail:** Describe the centralization gap. Example: `"Application logs written to local stdout only. 0 of 3 log sources forwarded to central location. No integrity verification."` or `"2 log sources centralized to CloudWatch but 0 integrity verification mechanisms (no HMAC, no immutable storage)."`
- **Remediation:** Implement log centralization with integrity checks. Using AWS CloudWatch + S3 + Athena:

  ```hcl
  # Central log group
  resource "aws_cloudwatch_log_group" "central_audit" {
    name              = "/audit/central"
    retention_in_days = 365

    tags = { Name = "central-audit-logs" }
  }

  # Log stream from multiple sources
  resource "aws_cloudwatch_log_stream" "cde_db" {
    name           = "cde-database"
    log_group_name = aws_cloudwatch_log_group.central_audit.name
  }

  # S3 export for immutable storage
  resource "aws_s3_bucket" "log_archive" {
    bucket = "audit-log-archive-${data.aws_caller_identity.current.account_id}"

    tags = { Name = "immutable-log-archive" }
  }

  # Prevent object deletion
  resource "aws_s3_bucket_object_lock_configuration" "log_archive" {
    bucket = aws_s3_bucket.log_archive.id

    rule {
      default_retention {
        mode = "GOVERNANCE"
        days = 2555  # ~7 years
      }
    }
  }

  # KMS key for encryption
  resource "aws_kms_key" "log_key" {
    description             = "Key for log encryption"
    deletion_window_in_days = 10
    enable_key_rotation     = true
  }

  resource "aws_s3_bucket_server_side_encryption_configuration" "log_archive" {
    bucket = aws_s3_bucket.log_archive.id

    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "aws:kms"
        kms_master_key_id = aws_kms_key.log_key.arn
      }
    }
  }
  ```

  For log integrity verification, use a log signing service:
  ```typescript
  // src/app/api/audit-log/route.ts
  import crypto from 'crypto';

  export async function POST(req) {
    const { event } = await req.json();

    // Create HMAC signature for integrity
    const hmac = crypto
      .createHmac('sha256', process.env.LOG_SIGNING_KEY)
      .update(JSON.stringify(event))
      .digest('hex');

    const signedLog = {
      ...event,
      signature: `sha256:${hmac}`,
      timestamp: new Date().toISOString(),
    };

    // Store in database and export to immutable storage
    await logCentralService.store(signedLog);

    return new Response(JSON.stringify({ signed: true }));
  }
  ```

## External references

- pci-dss Req 10.3
- pci-dss Req 10.4
- nist AU-9
- cwe CWE-778

Taxons: regulatory-conformance, observability

HTML version: https://auditbuffet.com/patterns/ab-001192
