Testing
The final chapter turns understanding into practice: how to test for CSRF on your own apps, the broken-defence patterns that look protected but aren't, a copy-pasteable hardening checklist — and the proper, deployable fix for the admin-login token error that started this whole course.
How to Test for CSRF
Testing your own endpoints follows a simple manual procedure (and tools automate it):
- Capture a legitimate state-changing request (DevTools → Network, or Burp Suite's proxy).
- Identify the auth — is it a cookie (CSRF-relevant) or a header token (not classic CSRF)? (Chapter 9)
- Strip the anti-CSRF token (and any custom header) and replay the request with only the cookie. If it still succeeds → vulnerable.
- Try a forged page — build an auto-submitting form (Chapter 3) on a different origin, load it while logged in, and see if the action fires.
- Test the token's strength — does the server accept an empty token? Another user's token? A token after it should have rotated?
The Broken-Defence Catalogue
These look protected but aren't — the patterns to hunt for in review:
| Broken defence | Why it fails | Chapter |
|---|---|---|
| Token rendered but never validated | server embeds it, never checks it on POST | 5 |
| Token only in a cookie | auto-rides forged requests; not the synchronizer pattern | 5 |
| State change on GET | forgeable by a bare <img>; SameSite=Lax still sends on top-level GET | 2,3,7 |
| Naive double-submit | cookie injection from a subdomain plants a known value | 6 |
| Validation on some routes only | one unprotected endpoint is enough | 5 |
| "CORS protects us" | CORS governs reads, not whether the request is processed | 2,4 |
| "We use JWT" | JWT in a cookie is auto-sent = full CSRF | 9 |
| Token accepted when empty/blank | missing-token check treats absent as valid | 5 |
The Hardening Checklist
__Host- cookie prefix if stateless; don't ship the naive variant- Stop caching the login form. Send
Cache-Control: no-store(andno-cache) on the login page so Firefox's bfcache doesn't restore a stale page carrying an out-of-date token. This is the most likely single fix given it's Firefox-specific. - Issue the token after session regeneration. If login rotates the session (anti-fixation), render/refresh the CSRF token after the new session exists, so the form's token matches.
- Prefer a per-session token for the login form (or one that tolerates refresh) rather than aggressive per-request rotation, which is what breaks on back/multi-tab.
- Graceful handling: on a token mismatch for the login specifically, re-render the login page with a fresh token instead of a hard 403 — so the user never sees the error.
Hands-On Exercises
Write a step-by-step manual test plan to determine whether a state-changing endpoint is CSRF-vulnerable, including the "remove the token and replay" check and a forged-page test. State what result at each step indicates a vulnerability.
📄 View solutionGiven the broken-defence catalogue, audit this app: it validates tokens on POST only, exposes GET /admin/delete?id=, stores its CSRF value only in a cookie, and the team says "CORS protects the API." List every CSRF flaw and the fix for each.
Write the complete, ordered fix for the admin-login "Invalid CSRF token" error: the headers to add, the session/token lifecycle change, and the graceful-handling behaviour. Explain why each addresses token staleness rather than weakening protection, and why the bug appeared in Firefox.
📄 View solutionChapter 10 Quick Reference
- Test by replay — remove the token/custom header, resend with just the cookie; if it works, the token isn't validated
- Tools: Burp Suite (CSRF PoC generator), OWASP ZAP; manual forged-page test from another origin
- Broken defences: token rendered-but-unvalidated · token-only-in-cookie · state-changing GET · naive double-submit · partial coverage · "CORS/JWT protects us" · empty-token accepted
- Checklist: tokens everywhere (library) · SameSite+Secure+HttpOnly · safe-GET · Origin check · re-auth on sensitive ops · prevent XSS · signed double-submit +
__Host-· test by replay - Login-error fix:
Cache-Control: no-storeon the login page · issue token after session regeneration · per-session token · re-render gracefully on mismatch - It's a stale token, not an attack — fix the staleness; never disable the check or loosen SameSite to "fix" it
- Test across browsers — Firefox's bfcache made the stale-token issue visible where others may differ
★ CSRF — Cross-Site Request Forgery Complete — 10 / 10 chapters
From the confused-deputy mechanism and ambient authority, through the attack anatomy, the CSRF/XSS/SSRF distinction, every defence (synchronizer & double-submit tokens, SameSite, Origin checks, re-auth), modern SPA/API/JWT considerations, and a deployable testing checklist. You can now recognize CSRF, defend against it in depth, audit broken defences — and you've fully diagnosed and fixed the admin-login token error that started it all.