# API access audit-logged

- **Pattern:** `ab-000397` (`api-security.design-security.audit-logging`)
- **Severity:** info
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000397
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000397)

## Why it matters

Audit logs are the difference between detecting a breach in hours and discovering it months later during an external notification. CWE-778 (Insufficient Logging) and OWASP API Security Top 10 2023 API9 (Improper Inventory Management / Insufficient Logging & Monitoring) both flag missing logs as an incident response failure, not just a monitoring gap. OWASP A09 2021 (Security Logging and Monitoring Failures) is one of the top-10 precisely because log absence is correlated with breach longevity. SOC 2 CC7.2, PCI DSS Requirement 10, and HIPAA audit controls all require API access to be logged with user, resource, and outcome. Logs with passwords or tokens embedded become a secondary breach surface — redact before writing.

## Severity rationale

Info severity because missing audit logs don't enable attacks directly, but they prevent detection and forensic reconstruction when other controls fail.

## Remediation

Add a structured logging middleware that writes one JSON line per request with user, endpoint, method, status, and duration. Redact sensitive fields before writing — never log Authorization headers, request bodies containing passwords, or PII.

```ts
// src/middleware.ts
export function middleware(req: NextRequest) {
  const start = Date.now()
  const res = NextResponse.next()

  res.headers.set('x-request-start', String(start))  // pass to route for duration calc
  return res
}

// src/lib/audit-log.ts
export function logApiAccess({
  userId, method, path, statusCode, durationMs
}: AuditEntry) {
  const entry = {
    timestamp: new Date().toISOString(),
    userId: userId ?? 'anonymous',
    method, path, statusCode, durationMs
    // DO NOT include: headers, body, tokens, passwords
  }
  // Write to structured log sink (stdout for Vercel/CloudWatch pickup)
  console.log(JSON.stringify(entry))
}
```

Ship logs to a persistent sink (CloudWatch, Datadog, Logtail) — in-memory logs are lost on restart.

## Detection

- **ID:** `audit-logging`
- **Severity:** `info`
- **What to look for:** Enumerate every relevant item. Check whether API requests are being logged with user ID, endpoint, timestamp, and response code. Verify that sensitive data (passwords, tokens, PII) are redacted from logs.
- **Pass criteria:** At least 1 of the following conditions is met. API requests are logged with user, endpoint, timestamp, and response code. Sensitive data is redacted. Logs are persistent and can be queried for security audits.
- **Fail criteria:** No logging of API requests, or logs include sensitive information like passwords or full request/response bodies.
- **Skip (N/A) when:** The API is internal-only and audit logging is not required.
- **Detail on fail:** `"No API request logging configured"` or `"Request logs include full request bodies with sensitive data"` or `"Logs stored in memory and cleared on restart — not persistent"`
- **Remediation:** Implement structured audit logging:

  ```ts
  const auditLog = (req, res, next) => {
    const startTime = Date.now()

    res.on('finish', () => {
      const duration = Date.now() - startTime
      const logEntry = {
        timestamp: new Date().toISOString(),
        userId: req.user?.id || 'anonymous',
        method: req.method,
        endpoint: req.path,
        statusCode: res.statusCode,
        duration
      }

      console.log(JSON.stringify(logEntry))
      // Store in persistent logging service (e.g., CloudWatch, ELK Stack)
    })

    next()
  }

  app.use(auditLog)
  ```

## External references

- cwe CWE-778
- cwe CWE-532
- owasp A09
- owasp API9

Taxons: observability

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