Cross-Site Request Forgery (CSRF)

CSRF tricks a logged-in user's browser into making an authenticated request to your site without the user intending it. The attack works because browsers automatically attach credentials — usually cookies — to requests, even ones triggered by a malicious page.

CSRF is fundamentally a cookie-based auth problem. If your app authenticates requests with Authorization: Bearer tokens that the browser doesn't send automatically, classic CSRF risk largely disappears (though you then inherit XSS-token-theft concerns instead).

TL;DR

Quick Example

The two layers that stop most CSRF — a SameSite session cookie plus a token on unsafe methods:

The server rejects any state-changing request whose token is missing or doesn't match.

How CSRF Works

Typical targets are high-value, state-changing actions: changing an email or password, adding a payment method, transferring funds, or deleting data.

When You're Vulnerable

You're at meaningful CSRF risk when all of these hold:

⚠️ CORS is not a CSRF defense. A cross-site POST can still be sent without CORS letting the attacker read the response — and for a destructive action, sending is enough.

Defenses (layered)

1. SameSite cookies

Set session cookies Secure, HttpOnly, and SameSite=Lax (or Strict if your UX allows). Lax blocks cookies on most cross-site requests while still allowing top-level navigations. Apps that need cross-site cookies must use SameSite=None; Secure, which raises the importance of the other layers.

2. Anti-CSRF tokens

For unsafe methods (POST/PUT/PATCH/DELETE): the server issues a token tied to the session, the client returns it in a header or form field, and the server validates it. The token must not be readable by third-party sites.

3. Origin / Referer checks

For unsafe methods, require the Origin header to match your site (fall back to a strict Referer check). This catches many attacks even if token wiring has a bug.

4. Double-submit cookie (stateless)

When you can't store server-side token state: set a random CSRF cookie and require the same value echoed in a request header. A third-party site can't read the cookie to copy it. Works best combined with SameSite.

Choosing a Defense

SPA Notes

Common Mistakes

Protecting only some endpoints

Relying on CORS or a JSON content type

FAQ

Do I need CSRF protection if I use JWTs in an Authorization header?

Generally no for classic CSRF — the browser doesn't auto-attach Authorization headers, so a cross-site request can't ride the user's credentials. You do still need to protect the token from XSS. The risk returns if you store the JWT in a cookie that's sent automatically.

Is SameSite=Lax enough on its own?

It's a strong baseline and blocks most cross-site sends, but it isn't complete — older browsers, SameSite=None flows, and some request types weaken it. Combine it with tokens and an Origin check.

Does CORS protect against CSRF?

No. CORS governs whether JavaScript may read a cross-origin response; it doesn't stop the request from being sent. A forged state-changing request can succeed regardless of CORS.

Which methods need CSRF protection?

State-changing ones: POST, PUT, PATCH, DELETE. Safe, idempotent reads (GET/HEAD) shouldn't change state, so they don't — and you should never perform mutations on a GET.

Related Topics

References