A09 Logging / Monitoring
A09 is Security Logging & Monitoring Failures. You can’t respond to what you can’t see. Without auth + admin + data-access logs, a breach has no detection path and a multi-month dwell time.
Structured logs + immutable storage + SIEM hooks
EXAMPLE
# 1) STRUCTURED logs (JSON) so SIEMs and grep both work
import json, logging, sys, time
logger = logging.getLogger('app')
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(logging.Formatter('%(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
def event(level, event_type, **fields):
payload = {
'ts': int(time.time() * 1000),
'level': level,
'event': event_type,
**fields,
}
logger.info(json.dumps(payload, default=str))
# Auth events
event('info', 'auth.login.success', user_id=u.id, ip=request.remote_addr, ua=request.headers.get('User-Agent'))
event('warn', 'auth.login.failed', email=email, ip=request.remote_addr, reason='wrong_password')
event('info', 'auth.logout', user_id=u.id)
event('info', 'auth.password_change', user_id=u.id)
event('info', 'auth.mfa.enabled', user_id=u.id)
# Authorisation failures (denied attempts → high-signal)
event('warn', 'authz.denied', user_id=u.id, target=resource, action=action)
# Admin / sensitive data
event('info', 'admin.user.role_changed', actor=admin.id, target=u.id, from='member', to='admin')
event('info', 'data.export', actor=u.id, rows=42, dataset='users')
# Anything you'd want to investigate later
event('error', 'integration.stripe.timeout', request_id=rid, took_ms=5210)
# 2) WHAT TO LOG (NIST 800-92 / OWASP cheat sheet)
# [ ] All authentication events (success + failure + lockouts)
# [ ] All authorization failures
# [ ] Admin actions + role / permission changes
# [ ] Privileged data exports / bulk reads
# [ ] Crypto failures (verify, decrypt failed, signature mismatch)
# [ ] Webhook signature failures
# [ ] Integration errors > some rate threshold
# Do NOT log: passwords, tokens, full PAN, full PII bodies
# 3) Ship logs OFF the host
# - CloudWatch / Datadog / Splunk / ELK / Grafana Loki
# - Immutable, retention ≥ 90 days (longer for regulated industries)
# 4) Alert on signals
# - 5 failed logins from one IP in 5 min
# - any 'authz.denied' on /admin/*
# - sudden burst of 500s with SQL in the message
# - 'data.export' outside business hours
# 5) Forward to a SIEM
# AWS — CloudWatch → Kinesis Firehose → S3 + Athena + EventBridge → Lambda alerts
# Cloud-agnostic — Vector / Fluent Bit → Loki / Elastic / Splunk
Why it matters
A09 is the OWASP entry that turns “breach” into “known issue.” The same code reviews catch SQLi; only logging catches the credential-stuffing wave the day it starts.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
// A09 Security Logging & Monitoring Failures — you don't know you've been breached. // Fix: structured logs, immutable retention, alerts on auth/crypto failures, IR runbooks.Try it Yourself »
Discussion
Loading…