How HTTP Works

Glossary Term

CSRF (Cross-Site Request Forgery)

CSRF tricks a logged-in browser into sending an unwanted request to your site. See how it works, why CORS does not stop it and which defenses actually do.

Reviewed 2 min readintermediate2 sourcesMarkdown
On this page

TL;DR: CSRF works because browsers attach cookies automatically, even to requests triggered by another site. Defend with SameSite cookies plus a CSRF token or Origin check on every state-changing request.

Cross-site request forgery (CSRF) is an attack in which a malicious page makes a victim’s browser send a request to a site where the victim is logged in. Because the browser includes that site’s cookies automatically, the request is authenticated, and the server cannot tell it from a deliberate action unless it checks for something the attacker cannot supply.

How it plays out

A page on evil.example contains a self-submitting form:

<form action="https://bank.example/transfer" method="POST">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit()</script>

If bank.example uses a session cookie without SameSite protection and checks nothing else, the request arrives like this:

POST /transfer HTTP/1.1
Host: bank.example
Origin: https://evil.example
Cookie: session=abc123
Content-Type: application/x-www-form-urlencoded

to=attacker&amount=1000

The Origin header gives the game away, but only if the server looks at it.

Defenses that work

  • Reject on Origin or Sec-Fetch-Site. For state-changing methods, require Origin to match your own origin, or Sec-Fetch-Site to be same-origin (or none).
  • CSRF tokens. A per-session or per-request random value in a hidden field or custom header that the attacker cannot read.
  • SameSite=Lax or Strict on the session cookie. Chromium treats a missing attribute as Lax; Firefox and Safari do not by default, so set it explicitly.
  • Use safe methods only for reading. Lax cookies are sent on top-level GET navigations, so a GET /delete?id=5 endpoint is still exploitable.

Non-obvious facts

  • CORS is not a CSRF defense. A cross-origin form POST or a “simple” fetch is sent whether or not CORS headers allow reading the response.
  • A custom header works as a defense (for example X-Requested-With) because adding one forces a preflight, which the attacker’s origin will fail. It only holds if CORS is configured strictly.
  • Same-site is not same-origin. A compromised sibling subdomain is same-site, so SameSite will not help; see Same-Site vs Same-Origin.
  • Login CSRF exists too. An attacker can log the victim into the attacker’s account, so protect the login form as well.

Go deeper

Frequently asked questions

What is CSRF?

Cross-site request forgery is an attack where another site causes a logged-in user's browser to send a state-changing request to your site, and the browser attaches the user's cookies automatically.

Does CORS prevent CSRF?

No. CORS controls whether a page can read a cross-origin response. A simple cross-origin POST is still sent with cookies, so the damage is done even if the attacker cannot read the reply.

Does SameSite=Lax fully protect against CSRF?

It blocks cookies on cross-site POSTs, but not on top-level GET navigations, and it does not stop requests from a sibling subdomain. Treat it as defense in depth alongside tokens or Origin checks.

Are APIs that use bearer tokens in a header vulnerable to CSRF?

Generally no, because the browser does not attach an Authorization header on its own. The risk exists when authentication relies on cookies or other ambient credentials.

Sources

  1. MDN Web Docs: Cross-site request forgery (CSRF)developer.mozilla.org
  2. OWASP CSRF Prevention Cheat Sheetcheatsheetseries.owasp.org

Keep going

Browse /search