The idea: you can't respond to what you never see
Every other item of the OWASP Top 10 is about stopping an attack. A09:2025 Security Logging and Alerting Failures is about the attacks you didn't stop: if nothing records them and nobody is told, an intruder can stay for months. In many breaches the company hears about it from someone outside (a bank, a customer, a researcher), not from its own monitoring. OWASP renamed the item in 2025, from "Monitoring" to "Alerting", to say that logs nobody acts on are not enough.
OWASP maps it to five CWEs, all shown on this page:
- CWE-778 Insufficient Logging: security events are not logged at all;
- CWE-223 Omission of Security-relevant Information and CWE-221 Information Loss or Omission: some events, or the fields that matter (who, from where), are missing;
- CWE-532 Insertion of Sensitive Information into Log File: passwords, session ids, card numbers in the log;
- CWE-117 Improper Output Neutralization for Logs: log injection.
Reading the canvas: on the top left is the client (mallory's attack script, or alice's browser), with each request and its reply. In the middle is the app, shop.example, with its logger. On the right is alerting: the central log store, three alert rules and the on-call engineer's pager. Below are the app's own app.log (red rows hold a secret or a forged line), the incident summary, and the timeline with attack events above the axis and alerts below it. Pink boxes are mallory's requests, blue ones alice's, grey ones log lines and events. The logging menu picks one of three setups; the attack is the same in all of them.
One attack, three setups
At 09:00 mallory replays five user/password pairs from a list leaked by another site; at 09:01 alice's reused password works; at 09:02 mallory probes /admin; at 09:04 she exports all 5,000 orders through the API; at 09:06 she changes alice's email so the password-reset mail goes to her. (The first two steps are the credential stuffing of A07 Authentication Failures.)
| Setup | Detected | By | Taken | Evidence |
|---|---|---|---|---|
| No logging (Demo 1) | day 212 | a bank tracing card fraud | 5,000 orders, alice's account | none |
| Plain log file, no alerts (Demo 2) | day 212 | the same bank | the same | deleted on day 30 by logrotate |
| JSON events + alerts (Demo 3) | 09:01 | rule R2, a page | nothing: blocked at 09:03 | 10 events, kept 365 days |
The plain log file is the common middle case: the app does log, so the box "logging" is ticked in an audit, but the file sits on the app host, holds only logins, and nobody reads it. When the bank calls on day 212, the evidence was rotated away on day 30. Worse, each failed-login line holds the password that was tried, so the log itself is a password list (CWE-532).
What to log, and what never to log
Log every security decision, whether it allowed or refused: logins and failed logins, access-control failures, input-validation failures, high-value actions (exports, payments, changes of email, password or permissions) and admin actions, including the responder's own. Each event needs enough context to investigate it later: when (UTC, synchronized clocks), who (user id), from where (IP, user agent), what (event type, path, object id) and the outcome.
| Log | Never log |
|---|---|
login_fail, login_ok with user and IP | the password, even a wrong one (a typo is one character from the real one: Demo 5) |
access_denied with user and path | session ids, access tokens, API keys |
| exports, payments, account changes | full card numbers, health data, other personal data the investigation doesn't need |
admin and responder actions (ip_blocked by dana) | encryption keys, connection strings |
In the plain setup the 403 on /admin is not logged at all: to the app it is not an error. That is CWE-223: the events that matter most to security often look normal to the code.
Alerts and time to detect
Logs only help if something reads them while the attack is happening. The JSON setup ships each event to a central log store (a SIEM, or any log platform), where three rules run:
- R1: 5 failed logins from one IP in 5 minutes → a ticket;
- R2: a successful login from such an IP → a page to the on-call engineer;
- R3: access denied on
/admin→ a ticket.
R2 is the one that matters: failures alone are background noise on any public login, but a success after a burst of failures from the same IP is almost always a takeover. The page reaches dana at 09:01; at 09:03 she blocks the IP and revokes alice's sessions, so the 09:04 export is refused (Demo 3). Time to detect drops from 212 days to 1 minute, and time to contain is 3 minutes. Her own action is logged too.
Log injection
A plain-text logger that pastes user input into a line lets the user write lines. mallory's user name is x%0a09:00 login ok user=admin: the %0a decodes to a newline, so app.log gets a second line saying an admin logged in (Demo 4). An investigator, or a rule matching "login ok", trusts it. The same trick can hide real lines, break a parser, or run script in a web-based log viewer that shows lines as HTML.
plain text: 09:00 login failed pw=guess user=x
09:00 login ok user=admin ← forged
JSON: {"ts":"09:00","ev":"login_fail","user":"x\n09:00 login ok user=admin","ip":"203.0.113.9"}
The fix (CWE-117) is to encode: a structured format such as JSON escapes the newline as \n inside the field, so one request is always one event. If you must write plain text, replace CR and LF, and encode for HTML when the log viewer is a web page.
Keeping logs safe and long enough
A log kept on the app host is lost when the host is rebuilt, rotated after a few days, and editable by the attacker who owns the host. Ship events off the host as they happen, to append-only (write-once) storage that the app's own credentials cannot delete. Keep them long enough to cover how long breaches go unnoticed: months, often a year or more, and what your regulator requires. Use one clock (NTP, UTC) on every host, and a request or trace id in every event so one request can be followed across services (see Distributed Tracing with OpenTelemetry).
Thresholds and alert fatigue
An alert on every failed login would page someone hundreds of times a day, and after a week nobody would look. alice's single typo (Demo 5) is logged but stays at 1 of 5 on R1: no alert. Good rules fire on what a real user rarely does (a success after many failures, a first-ever admin request, a huge export), page only for those, and send the rest to tickets or dashboards. Every page should have a playbook: what to check, how to block, who to tell. Honeytokens help too: a fake admin account or a fake API key that no real user ever touches, so any use of it is an alert with no false positives.
How to prevent it
- Log every login, failed login, access-control failure, input-validation failure and high-value action, with time, user, IP and outcome.
- Never log passwords, session ids, tokens or full card numbers; mask what you need to keep.
- Use a structured format (JSON) and encode every field; one request, one event.
- Ship logs off the host to an append-only central store; keep them long enough for an investigation.
- Write alert rules for the attacks you expect (stuffing, takeover, admin probing, mass export), with thresholds that a real user doesn't reach.
- Make the alerts reach a person, with a playbook; test them by running the attack yourself.
- Have an incident response plan (for example NIST SP 800-61) and practise it.
OWASP Top 10:2025 series
- A01 Broken Access Control
- A02 Security Misconfiguration
- A03 Software Supply Chain Failures
- A04 Cryptographic Failures
- A05 Injection
- A06 Insecure Design
- A07 Authentication Failures
- A08 Software or Data Integrity Failures
- A09 Security Logging and Alerting Failures (this page)
- A10 Mishandling of Exceptional Conditions
What the page leaves out
Real attacks are slower and spread over many IPs, so real rules count per account and per network as well as per IP, and the 2-minute response assumes someone is awake. The page leaves out log levels and sampling, log shippers and their buffering (events lost when the host dies), SIEM correlation and anomaly detection, alert routing and escalation, tamper-evident logs (hash chains, signed batches), privacy rules on what may be kept and for how long, client-side and infrastructure logs (firewall, DNS, cloud audit trails), and the cost of storing a year of events.