The Engineering Codex/Application Security Engineering
DAY 4
06 / 09

OWASP Top 10:2025 — Part II + XSS & CSRF

schedule8 minsignal_cellular_altIntermediate1,665 words
A06 through A10 of the 2025 list — insecure design, authentication failures, software/data integrity, logging & alerting, and the new A10:2025 (Mishandling of Exceptional Conditions). Plus a deep dive on XSS and CSRF, the two web bugs every engineer should be able to draw on a whiteboard.

What you will learn

01A06:2025 — Insecure Design
02A07:2025 — Authentication Failures
03A08:2025 — Software or Data Integrity Failures
04A09:2025 — Security Logging & Alerting Failures
05A10:2025 — Mishandling of Exceptional Conditions NEW
06Bonus: XSS — Cross-Site Scripting

The first half of the 2025 list is about what you wrote and what you shipped with. The second half is about what happens around it: the design choices you didn't make, the auth corners you cut, the integrity checks you skipped, the telemetry you forgot, and — new in 2025 — the exceptions you let escape. We close with the two web-specific bugs (XSS, CSRF) that don't have their own Top 10 slots but underpin huge swathes of A01, A05, and A07.

A06:2025 — Insecure Design

Down from #4 in 2021. The category covers bugs that are architecturally wrong — no amount of patching makes them safe; the design is the bug. A few canonical examples:

  • Password reset that emails the password back in plaintext.
  • Two-factor reset that requires only the original factor ("forgot your token? we'll text the same number").
  • An API that accepts the price of a cart from the client and trusts it.
  • Rate limiting bolted on per-IP when the abuse vector is per-account.
  • Caching authenticated content on a shared CDN.
  • Object-permission checks delegated to the front end.

The fix is upstream of code: threat modelling done early (Day 1 PM), secure design patterns (server-authoritative state, pre-built libraries for tricky flows, defence-in-depth), and abuse-case stories alongside user stories. OWASP recommends practicing it with the SAMM and Cornucopia card games — they sound silly; they work.

A07:2025 — Authentication Failures

Renamed from "Identification and Authentication Failures" to just Authentication Failures for clarity. Top patterns are the same — see Day 2 PM for the deep dive. The 2025 update emphasises:

  • Brute-force / credential stuffing without rate limiting or breach-list checks.
  • SMS-based MFA where SIM swap is in scope (NIST has effectively deprecated SMS for high-assurance use).
  • Predictable session IDs, or rotating them at the wrong moment (post-login session fixation).
  • Long-lived bearer tokens that survive logout.
  • "Forgot password" emails with predictable or non-expiring tokens.
  • WebAuthn / passkeys not supported, leaving phishing-resistant auth off the table for users who want it.
Quick check
After a successful login, your app keeps the same session ID it generated for the anonymous visitor. Why is that dangerous?
Show answer
Session fixation. An attacker who can plant a session ID on the victim's browser before login (via a same-origin issue, an open redirect, or a public terminal) inherits the post-login session. Always rotate the session ID on auth-state transitions: anonymous → authenticated, role-up, MFA-step-up.

A08:2025 — Software or Data Integrity Failures

The category covers two related ideas: code or data flowing into your system without integrity checks, and CI/CD pipelines treated as trusted backdoors. SolarWinds (2020) is still the canonical example — a build pipeline was compromised and a signed update shipped malware to 18,000 customers.

  • Auto-update without signature verification.
  • CDN-delivered scripts without integrity="sha384-…" SRI tags.
  • Insecure deserialisation (Java ObjectInputStream, Ruby Marshal, Python pickle).
  • CI runners that mount workspace secrets and pull untrusted code from PRs.
  • Webhook receivers that don't verify HMAC signatures (or use == for constant-time comparison).

A09:2025 — Security Logging & Alerting Failures

Renamed from "Logging and Monitoring Failures" — the new title makes the point: logs without alerts are file storage. If you cannot answer "who did what, when, on which resource" within minutes, and have an alert on the things that matter, you have an A09 problem. The 2025 OWASP report still measures median time-to-detect a breach in weeks, almost always because the logs you needed weren't captured, weren't retained, or weren't searchable.

The minimum bar for any application:

  • Authentication, authorization, and admin events to a central, append-only log.
  • Sensitive operations logged with actor + target + result, never with secrets in the body.
  • Logs shipped off-host (an attacker rooting a box must not delete the audit trail).
  • Alerts on key signals: login spikes, privilege changes, mass-export, error-rate inversions.
  • Retention long enough for incident response — often 90 days hot, 1 year cold.
  • SIEM / detection rules tested against the same playbook you'd run during an incident.

A10:2025 — Mishandling of Exceptional Conditions NEW

Brand new in 2025. It absorbs the slot SSRF vacated and covers 24 CWEs around improper error management. OWASP's framing: "programs fail to prevent, detect, and respond to unusual and unpredictable situations, leading to crashes, unexpected behaviour, and sometimes vulnerabilities." In other words, unhandled errors become an attack surface.

When an exception escapes — what happens next? Unusual conditiontimeout · NULL · 5xx · disk full Caught? handled?often: no, no, no crash → DoS stack trace → info disclosure caught & ignored → silent corruption fail-open → bypass auth A10:2025 takeawayAn unhandled exception is not a bug; it is an attack surface.Catch close to the source. Fail closed. Log; never leak.
A10:2025 collects 24 CWEs covering NULL deref, swallowed exceptions, fail-open paths, and verbose error pages.

The Four Anti-Patterns the Category Calls Out

  1. Catching too broadly and ignoring (except Exception: pass, catch (Throwable t) {}) — corrupt state continues silently. CWE-755, CWE-390.
  2. Failing open — when authorisation, signature verification, or rate limiting throws, allowing the request "to avoid breaking the user experience." CWE-636.
  3. Verbose error pages in production — stack traces with table names, file paths, secrets in env. CWE-209.
  4. Resource leaks under error — connections, locks, temporary files left dangling, leading to resource exhaustion DoS. CWE-404.
❌ Mishandled
python
def verify_signature(payload, sig):
    try:
        return hmac.compare_digest(
            sign(payload), sig
        )
    except Exception:
        return True   # fail open!
✅ Handled
python
def verify_signature(payload, sig):
    try:
        return hmac.compare_digest(
            sign(payload), sig
        )
    except (TypeError, ValueError) as e:
        log.warning("sig verify error", exc_info=e)
        return False   # fail closed

Prevention Strategy

  • Catch close to the source with specific exception types — not bare except.
  • Always fail closed on security-relevant operations (signature verify, authorisation lookup, decryption, rate limiting).
  • Centralised exception handler that returns a generic 500 in production while logging the full stack with a correlation ID.
  • Validate inputs at trust boundaries before they reach the parser that might throw.
  • Use transactions / context managers so resources release even on error (Python with, Java try-with-resources, Go defer).
  • Test the unhappy paths — chaos / fault-injection tests catch the patterns SAST cannot.

Bonus: XSS — Cross-Site Scripting

XSS is now folded into A05 (Injection) but deserves its own section because it remains the single most-found web bug in bug bounties. Three flavors:

Reflected
  • Payload in URL → echoed back unescaped
  • Requires the victim to click a crafted link
  • ?q=<script>…
Stored
  • Payload saved (comment, profile bio)
  • Triggers for every viewer
  • Worst impact

And DOM-based — the dangerous sink is in client JS (innerHTML, document.write, eval). The fix in modern frameworks: don't disable React/Vue/Angular's auto-escaping. dangerouslySetInnerHTML earns its name.

attacker <img src=x onerror=fetch(`/exfil?c=${document.cookie}`)> stored as-isno output encoding victim views page cookie / token exfiltrated HttpOnly cookies cannot be read by JS — use them.
Stored XSS in three steps. Output encoding and Content-Security-Policy break this chain.

The Three-Layer XSS Defence

  • Output encoding appropriate to context — HTML body, attribute, JS string, URL, CSS. Frameworks usually do this for you; don't disable it.
  • CSP — a strong Content-Security-Policy with nonces or hashes blocks inline JS even if a payload lands.
  • HttpOnly cookies — at minimum, make session theft impossible from a successful XSS.

Bonus: CSRF — Cross-Site Request Forgery

The browser sends your cookies on every request to your domain — including requests issued by JavaScript on another site. CSRF abuses that ambient authority: a malicious page makes the victim's browser perform a state-changing action against your site while logged in.

victim's browser evil.example<form auto-submit> bank.exampletrusts cookie POST https://bank.example/transfer Cookie: sid=alice… to=evil&amount=10000 Browser attaches cookie automatically. Bank can't tell this came from evil.example.
CSRF in one diagram. The cookie is sent because the browser sends it — not because the user wanted to.

The CSRF defence stack

  • SameSite=Lax (or Strict) on session cookies — solves CSRF for top-level navigations in modern browsers.
  • Anti-CSRF tokens — synchroniser-token or double-submit-cookie pattern, validated on every state-changing request.
  • Custom-header check — for pure-API JSON endpoints, require an X-Requested-With or Origin match. Browsers refuse to add custom headers cross-site without a CORS preflight.
  • Re-authentication on truly destructive actions (delete account, change password, send wire).
Flashcard
XSS and CSRF: which one defeats which? Why are both mitigations needed?
Click to flip ↻
Answer
XSS runs in the victim's origin — same-origin to your site — so SameSite cookies and CSRF tokens do not stop it; an XSS payload simply fetches your real CSRF token and submits the form. CSRF defences protect against cross-origin abuse; XSS defences (output encoding, CSP, HttpOnly cookies) protect against script execution. You need both because they cover orthogonal threats.
Mnemonic — A06 to A10 (2025)
"Bad blueprints, weak login, broken signatures, blind logs, swallowed errors."
  • A06 Bad blueprints — Insecure Design
  • A07 Weak login — Authentication Failures
  • A08 Broken signatures — Software/Data Integrity
  • A09 Blind logs — Logging & Alerting
  • A10 Swallowed errors — Mishandling Exceptional Conditions
🔑
Key takeaways
1) A06 is what threat modelling prevents — design before code. 2) Sessions and tokens need rotation, expiry, and rate limits, not just AuthN. 3) Integrity failures hide in CI/CD; sign and verify everything that crosses a build boundary. 4) Logging is a security control — design it for incidents, with alerts on the things that matter. 5) A10 is new and load-bearing: catch close to source, fail closed, never leak stack traces. 6) XSS, CSRF and SSRF are three different bugs with three different defences; never substitute one for another.

Finished reading?