Back to Cheat Sheets

📊 Insufficient Logging & MonitoringAPI 2019

OWASP API Security Top 10 2019 - API10

HIGH RISK

📋 What Is It?

Insufficient Logging & Monitoring is not a flaw an attacker exploits - it is the flaw that lets every other attack proceed undetected. When an API fails to record security-relevant events, fails to centralise and watch those records, and fails to alert and respond, an intruder can probe, enumerate, brute-force and exfiltrate for weeks with nobody noticing.

Three linked failures define the category, and any one of them is enough to be blind: logging (events never written, or written without context), monitoring (records scattered, never centralised, never watched), and alerting & response (no threshold fires, no human is paged). The measure of the problem is dwell time - how long an attack runs before it is seen.

API10 OWASP Rank 2019
Dwell Time Multiplier
3rd party Usually Tells You

⚠️ Common Exploits

  • Silent Failures: failed logins and 403s never recorded - brute force is invisible
  • No Context: free-text lines with no client id, object id, or source
  • Local-Only Logs: never shipped, rotated away, deletable by the intruder
  • No Thresholds: a 10,000-request scrape triggers nothing
  • Unusable Format: unparseable text that no query can aggregate
  • Clock Drift: unsynchronised timestamps make correlation impossible
  • Over-Logging Secrets: tokens and PII written into logs, creating a new breach target

🔴 Attack Flow

1. Probe quietly and watch for any defensive reaction
↓
2. Note that failures return errors but produce no lockout, no challenge, no contact
↓
3. Escalate volume - enumerate objects, stuff credentials, scrape data
↓
4. Persist: nothing correlates the activity, nothing pages a human
↓
5. IMPACT: Months of undetected access - the breach is reported by an outsider!

❌ Vulnerable Code

# Bad: the security event disappears if not check_password(user, pw): return {'error': 'Invalid credentials'}, 401 ← nothing logged at all // Bad: unstructured, contextless, and unparseable console.log('error in handler'); ← who? what object? from where? # Bad: authorization denials swallowed except PermissionError: pass ← BOLA enumeration leaves no trace // Bad: secrets written into the log - a new breach target logger.info(`login ${email} token=${accessToken} card=${pan}`); # Bad: logs stay on the box, world-writable, rotated after a day # /var/log/api.log → never shipped, never read, deletable by the intruder

✅ Secure Code

# Good: structured, contextual, machine-parseable security events log.warning('auth.login.failed', extra={ 'event': 'auth.login.failed', 'subject': hash_id(email), # identify without storing the raw value 'source_ip': request.remote_addr, 'user_agent': request.user_agent.string, 'request_id': g.request_id, 'ts': datetime.now(timezone.utc).isoformat(), }) // Good: log the authorization denial that enumeration depends on if (!canAccess(user, obj)) { logger.warn({ event: 'authz.denied', subject: user.sub, objectId: obj.id, requestId: req.id }); return res.sendStatus(404); } # Good: redact before the record leaves the process logger.addFilter(RedactFilter(fields=['password', 'token', 'authorization', 'pan'])) # Good: centralise, protect, synchronise, and alert # ship to SIEM (append-only, retained 12m), NTP-synced clocks # alert: >20 auth.login.failed per account / 5m, or >100 authz.denied per token / 5m # each alert maps to a runbook with a named owner and an escalation path

✓ Prevention Checklist

  • Log every security-relevant event: authn, authz, validation, rate-limit, admin action
  • Use structured logs (JSON) with a stable event taxonomy
  • Include who, what object, from where, when, and a request id
  • Never log credentials, tokens, or raw PII - redact at the source
  • Ship logs off the host to append-only, access-controlled storage
  • Synchronise clocks (NTP/UTC) so events correlate across services
  • Set tuned thresholds on 401/403/429 spikes and enumeration patterns
  • Route alerts to a named owner with a runbook, not to a mailbox
  • Define retention that outlives realistic dwell time
  • Rehearse: run an attack simulation and confirm the alert actually fires

🔍 Detection & Tools

Pipeline:

ELK / OpenSearch Loki + Grafana Splunk Fluent Bit OpenTelemetry

Detection & Response:

Sigma rules Wazuh Falco PagerDuty runbooks

How to Test:

  • Run 100 failed logins - does an alert fire, and how fast?
  • Enumerate object ids and check the 403/404s are recorded and correlated
  • Grep the log store for tokens, passwords, and PII that should be redacted
  • Confirm logs survive host loss and cannot be edited in place
  • Compare timestamps across services for clock drift

🌍 Real-World

These are documented incident classes - recurring patterns rather than single CVEs.

  • Breach found by a third party: the victim learns of months-old access from a researcher, a customer, or data appearing for sale.
  • Invisible enumeration: hundreds of thousands of object-id probes that produced 403s nobody recorded or counted.
  • Logs destroyed with the host: local-only logs lost when the compromised instance was terminated, leaving no forensic trail.
  • Alert fatigue: thresholds so noisy that the real signal was muted months before the incident.
  • Logs as the breach: tokens and card data written to a log store with far broader access than the database it came from.

📌 Quick Tips

  • DO NOT swallow authorization failures
  • DO NOT log secrets or raw PII
  • DO emit structured events with full context
  • DO centralise, protect, and retain them
  • DO alert on thresholds and page a real owner
  • DO test detection the way you test features

📜 Compliance

Related Standards:

  • OWASP API Security Top 10 2019 - API10
  • 2023 note Folded into API9/API10 guidance and general operational security
  • CWE-778 Insufficient Logging
  • CWE-223 Omission of Security-relevant Information
  • CWE-532 Insertion of Sensitive Information into Log File
  • NIST SP 800-92 Log Management