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.
Info severity because missing audit logs don't enable attacks directly, but they prevent detection and forensic reconstruction when other controls fail.
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.
// 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.
ID: api-security.design-security.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:
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)