07 — Audit Logging
Non-repudiation and forensics: a complete, attributable, tamper-evident record of security-relevant activity. Closes STRIDE Repudiation and detects the rest. Builds on tamper-evident log (product/02 E2) and the event backbone (ADR-0006).
1. What we log (and what we never log)
| Log | Don’t log |
|---|---|
| authN events (login, MFA, failures, token issue/refresh/revoke) | passwords, secrets, API keys, tokens |
| authZ decisions — especially denials | file contents |
| admin actions (role/grant changes, member add/remove) | full PII in the clear (minimize; hash/pseudonymize identifiers) |
| data access (read/write/move/delete/share/download) | sensitive values in URLs/params (CWE-532) |
| key & secret operations (create/rotate/revoke/use) | |
| policy & config changes; exports; tenant lifecycle |
Each entry: who (principal incl. service account), what (action), which (resource), when, where (IP/device), result, tenant, and the trace ID (ADR-0013) — so an audit entry links to the full distributed trace.
2. Tamper-evident, separate-domain log
flowchart LR
classDef e fill:#fde68a,stroke:#b45309,color:#111827;
classDef d fill:#bbf7d0,stroke:#15803d,color:#111827;
act["security-relevant action"]:::e --> ob["outbox (same txn as the action, ADR-0006)"]:::e
ob --> log["append-only Merkle audit log (separate trust domain, object-lock)"]:::d
log --> sth["periodic signed tree head (published)"]:::d
log --> siem["SIEM export (Splunk/Datadog/Elastic)"]:::d
sth --> proof["inclusion + consistency proofs: prove nothing was edited or removed"]:::d
log --> alert["anomaly alerting (cross-tenant attempts, mass download, priv changes)"]:::d
- Tamper-evident: an append-only Merkle log (RFC 6962 / Certificate-Transparency style) with periodic signed tree heads lets anyone verify no entry was altered or deleted — even by an admin or rogue DBA (product/02 E2).
- Separate trust domain: shipped to an isolated, append-only store with object-lock (platform/12) — never rely on cloud-native logs alone; a prod compromise must not reach the audit trail.
- Written via the outbox in the same transaction as the action → no lost or phantom audit entries (the same correctness property as domain events, ADR-0006).
3. Detection (audit is also a sensor)
Stream to SIEM (product/08 EV4) and alert on: cross-tenant access attempts (the canary, 05), failed-auth spikes (brute force), mass downloads/exports (exfiltration), privilege changes, key operations, impossible-travel logins, and abnormal service-account behavior.
4. Compliance & privacy of the log itself
- Retention per policy/regulation (e.g. SOC2/HIPAA windows), tiered to cold storage (storage/10).
- Audit logs contain personal data → access-controlled (
auditorrole only, 04), tenant-scoped, and themselves covered by GDPR (11); minimize PII, pseudonymize where possible. - Immutability vs erasure tension: GDPR erasure vs append-only audit → log pseudonymous IDs so a subject can be erased from data stores while the audit trail’s integrity remains (resolve the key, not the entry).
5. Threats addressed & residual
| Threat | Control | Residual |
|---|---|---|
| Repudiation (“I didn’t do that”) | attributable, complete audit | low |
| Audit tampering (insider/rogue DBA) | Merkle tamper-evidence + separate domain | low |
| Undetected breach | SIEM + anomaly alerting | medium (detection coverage) |
| Log-based info disclosure | no secrets/PII-in-clear; access-controlled | low |
References
- RFC 6962 (Certificate Transparency / Merkle logs): https://www.rfc-editor.org/rfc/rfc6962
- OWASP Logging Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html