iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up
Next »

Summary

A condensed summary of CSRF: what it is, why same-site cookies are the modern primary defence, when you still need synchronizer tokens, how SPAs change the picture, and what NOT to bother with. Keep it open during a security review and you will catch most issues in one pass.

CSRF in one screen, plus the decision tree

EXAMPLE
# CSRF in one screen

## What is CSRF?
A user is logged in to site A. The user visits attacker.com which, behind the scenes,
forces the user's browser to send a state-changing request to site A. Because the
browser attaches site A's auth cookie automatically, the request succeeds — under
the user's identity, without the user noticing.

## The four defences (use the first that applies)
1. **SameSite cookies (Lax or Strict)** — the modern primary defence
   - Lax:    cookie sent on top-level GETs only; blocks the classic 'invisible POST'
   - Strict: cookie never sent on cross-site requests, period
   - Set on every authenticated cookie. Most browsers default to Lax now, but explicit beats implicit.

2. **Synchronizer token** — still mandatory for high-risk apps
   - Server generates a random per-session token, embeds it in forms (hidden field) or returns it for JS
   - Server verifies the token on every state-changing request
   - Frameworks ship this: Laravel @csrf, Rails CSRFToken, Django {% csrf_token %}, Spring CookieCsrfTokenRepository

3. **Custom request header (bearer-token APIs)**
   - Browsers do NOT attach custom headers on cross-origin form submissions
   - If your API requires `Authorization: Bearer X-Y-Z` and rejects requests without it, CSRF is structurally impossible

4. **Origin / Referer check** — last-resort heuristic
   - Verify Origin (preferred) or Referer matches your expected hosts
   - Fragile (referrer policies, privacy modes), but free defence-in-depth

## The decision tree for new features
- Is this a state-changing endpoint? -> if no, you don't need CSRF defence
- Cookie-authenticated? -> require SameSite=Lax + a synchronizer token
- Bearer-token / API key only? -> require the custom header, that is enough
- Public endpoint (no auth)? -> nothing to forge; no CSRF surface

## Things that do NOT help (do not bother)
- Checking the User-Agent header
- Requiring POST (the attack uses POST too)
- Captcha on every action (UX wins beat once)
- Custom obscure routes (security through obscurity is not security)

## SPA-specific notes
- If your SPA uses cookies, set sameSite=lax + the synchronizer token via a `XSRF-TOKEN` cookie
  and have the SPA echo it back as `X-XSRF-Token` (Laravel/Angular do this by default)
- If your SPA uses Authorization: Bearer, you do NOT need a CSRF token
- Disable CORS credentials unless absolutely required; if required, the allowed-origins list MUST be
  exact (not wildcards)

## Quick review checklist
- [ ] Cookies have SameSite=Lax (or Strict) and Secure=true
- [ ] Every state-changing route is POST/PUT/PATCH/DELETE
- [ ] No GET handler mutates state
- [ ] Either: synchronizer token on cookie-auth, OR custom header on bearer-auth
- [ ] CORS allowed_origins is an exact list, not '*'
- [ ] Test: a forged HTML form posting to /account/delete from another origin fails
- [ ] Test: an XHR from another origin without the header fails

## Common bugs found in review
- A 'delete account' link that uses GET (so img tags can trigger it)
- A `whitelist exempt` on the CSRF middleware that quietly disabled it for a webhook,
  then accidentally exempted the broader path
- SameSite=None used to fix a payment flow, but the secure flag was forgotten
- A SPA that copies Authorization tokens into a cookie 'for convenience' — now CSRF-vulnerable

Why it matters

When SameSite=Lax is honoured by all your supported browsers and you also issue a synchronizer token, you have belt-and-braces. Cut to one only when you cannot avoid it (legacy embed, third-party iframe), and document the gap so the next reviewer knows it is a deliberate tradeoff.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
// Next: cross-site cookie partitioning (CHIPS), site isolation, FedCM.
Try it Yourself »

Discussion

Loading…

Next »