| name | design-security-logging |
| description | Use when adding logging to authentication flows, authorization decisions, admin actions, or any security-sensitive operation — to enable detection, forensics, and incident response. |
| source | OWASP Logging Cheat Sheet (owasp.org/www-project-cheat-sheets); OWASP Top 10 2021 A09; NIST SP 800-92; CWE-778; PCI DSS v4.0 Requirement 10 |
| tags | ["security","owasp","logging","monitoring","audit-trail","incident-response","developer","devops"] |
| related | ["apply-risk-based-qa-scaling"] |
Design Security Logging
Log security-relevant events with sufficient context for forensics while preventing log injection and avoiding logging sensitive data — enabling detection and investigation of attacks, breaches, and insider threats.
Why This Is Best Practice
Adopted by: OWASP Top 10 2021 A09 (Security Logging and Monitoring Failures) is its own category. PCI DSS v4.0 Requirement 10 mandates audit logging for cardholder data environments with 12-month retention. NIST SP 800-92 (Guide to Computer Security Log Management) is the authoritative federal reference. SOC 2 Type II, ISO 27001, and HIPAA all require audit logging with tamper evidence. AWS, GCP, and Azure provide CloudTrail/Cloud Audit Logs/Activity Logs as core compliance features.
Impact: The Equifax breach (2017, 147M records) went undetected for 76 days partly due to insufficient logging — OWASP A09 was directly cited in post-mortem analysis. Capital One breach detection (2019) was triggered by an AWS CloudTrail anomaly alert, limiting the window. NIST estimates proper logging reduces mean time to detect (MTTD) breaches by 63 days on average (IBM Cost of a Data Breach Report 2023).
Why best: print() or unstructured log lines are the alternative — they're unsearchable at scale, don't aggregate, and lack the structured fields needed for SIEM correlation. Structured JSON logs with consistent field names enable automated alerting, SIEM ingestion (Splunk, DataDog, Elastic), and cross-service correlation of attack chains.
Sources: OWASP Logging Cheat Sheet; NIST SP 800-92; IBM Cost of Data Breach 2023; PCI DSS v4.0 Req 10; CWE-778
Steps
-
Log these security events at minimum:
| Event | Log level | Fields required |
|---|
| Login success | INFO | user_id, ip, user_agent, timestamp |
| Login failure | WARN | username_attempted, ip, reason, timestamp |
| Logout | INFO | user_id, session_id, timestamp |
| Password change | INFO | user_id, ip, changed_by, timestamp |
| Permission change | WARN | user_id, changed_by, old_role, new_role, timestamp |
| Admin action | WARN | admin_id, action, target, ip, timestamp |
| Access denied (403) | WARN | user_id, resource, ip, timestamp |
| Input validation failure | INFO | ip, endpoint, field, reason |
-
Use structured logging with consistent fields:
import structlog
import logging
logger = structlog.get_logger()
def log_auth_event(event, user_id, ip, **kwargs):
logger.info(
"auth_event",
event=event,
user_id=user_id,
ip=ip,
timestamp=datetime.utcnow().isoformat() + 'Z',
**kwargs
)
log_auth_event("login_success", user_id=42, ip=request.remote_addr)
log_auth_event("login_failure", user_id=None, ip=request.remote_addr,
username_attempted=username[:50], reason="invalid_password")
-
Never log sensitive data — these must never appear in logs:
- Passwords, password hashes
- Full credit card numbers (PAN) — PCI DSS prohibits this
- Session tokens, API keys, JWTs
- Full SSN, full account numbers
- Health data (HIPAA)
logger.info(, password=request.form[])
logger.info(, user_id=user., success=)
():
* ((value) - visible) + value[-visible:]
logger.info(, last4=mask(card_number))
Rules
- Log the who, what, when, where, and outcome — missing any one makes forensics incomplete.
- Use UTC timestamps with millisecond precision and include timezone offset — local time creates ambiguity during DST.
- Log IDs, not names —
user_id=42 is better than username=alice for joining with DB records during investigation.
- Failure to log is a security control failure — treat logging errors as security incidents.
Common Mistakes
- Logging full request bodies — may contain passwords, tokens, or PII.
- Using
print() or unstructured strings — unsearchable, doesn't aggregate, no structured fields for SIEM.
- Only logging errors, not security events — a successful brute force that guesses correctly generates no error; login success must also be logged.
- Storing logs on the same host as the application — an attacker who compromises the app can erase the evidence.