Skip to content
atlas

CSRF token

Also known as: anti-CSRF token, synchronizer token

A secret, hard-to-guess value a site puts in its own forms and checks on every change, so requests started by other sites fail.

Draft - this entry has not been reviewed yet.

Formal

A random value the server ties to the user's session and places in each page or form it sends; any request that changes data must return the value, and the server refuses requests where it is missing or wrong.

In plain English

Like a shop that stamps a one-off number on every order form it hands out, and only accepts forms that come back with the number it gave you.

In practice

A pension fund's member portal puts a hidden field with a fresh value in its form for changing the payout account; a forged change sent from another site carries the login cookie but not the value, so the portal rejects it.

Why it matters

The browser sends cookies by itself, so a cookie alone cannot prove the user meant to act; without a second proof, any site the user visits can act in their name.

How to put it into practice

The usual steps, in order. Adapt them to your organisation.

  1. List every request that changes state (POST, PUT, PATCH, DELETE), and make sure no GET request changes data.
  2. Turn on the framework's built-in CSRF protection, such as the anti-forgery tokens in ASP.NET Core, Django or Spring Security, instead of writing your own.
  3. Generate the token with a cryptographically secure random generator, tie it to the user's session and issue a new one when the user logs in.
  4. Put the token in a hidden form field or, in a single-page app, send it in a custom header such as X-CSRF-Token, and never put it in a URL.
  5. Check the token on the server for every state-changing request with a constant-time comparison, and reject the request when it is missing or wrong.
  6. If the application is stateless, use the signed double-submit cookie pattern with an HMAC bound to the session, and give the cookie the __Host- prefix.
  7. Add defence in depth with SameSite=Lax or Strict on session cookies and a check of the Fetch Metadata or Origin headers on unsafe requests.
  8. Test that a forged request from another origin is rejected, and keep that test in the automated suite so a new endpoint cannot skip the check.

Common pitfalls

  • Exempting single endpoints, such as JSON or file-upload endpoints, from the CSRF check to make a client work.
  • Using the naive double-submit cookie without a signature, which an attacker who controls a subdomain can defeat.
  • Relying on SameSite cookies alone, although requests from sibling subdomains count as same-site and still get through.
  • Believing the token helps against XSS, when any script running on the page can read it.

Good guides

Technical deep dive

The synchronizer token pattern is the stateful form: the server generates a large unpredictable value with a cryptographically secure random number generator, stores it in the server-side session and embeds it in every form as a hidden field or exposes it to JavaScript through a meta tag. On every unsafe request (POST, PUT, PATCH, DELETE) the server compares the submitted value with the stored one using a constant-time comparison and rejects the request on mismatch or absence. Per-request tokens shorten the window in which a leaked token is useful, and OWASP rates them as more secure, but they break the back button and multiple open tabs, so per-session tokens are the common default.

Stateless applications use the double-submit cookie pattern: the token is sent both as a cookie and as a request parameter or header, and the server checks that the two match. The naive version is weak, because an attacker who can write cookies for the domain, for example from a compromised or vulnerable subdomain or through a man-in-the-middle on plain HTTP, can plant a matching pair. OWASP therefore recommends the signed double-submit variant, where the token is an HMAC, keyed with a server secret, over a session-bound value that changes at each login and a random value, so a planted cookie fails verification. Cookie name prefixes such as __Host- further prevent subdomains from overwriting the cookie.

Where the token travels matters. It must never appear in a URL, since URLs leak through Referer headers, logs and browser history. Single-page applications typically read it from a cookie or an endpoint and send it in a custom header such as X-CSRF-Token or X-XSRF-TOKEN (the convention Angular's HttpClient uses). Frameworks mask the token on each render, XOR-ing it with a fresh random pad as Django and Rails do, so that the value in an HTTPS-compressed response cannot be recovered with a BREACH-style length side channel.

A CSRF token is not an authentication secret and offers no protection against XSS: any script running in the same origin can read it from the DOM. It also does nothing for requests authenticated with a bearer token in an Authorization header, which browsers do not attach automatically, so APIs that do not use cookies usually do not need one. Regenerating the token when the user logs in avoids session fixation-style reuse of a token obtained before authentication.

What to learn first

Everything this builds on, foundations first.

  1. Digital identity
  2. →Network
  3. →Credential
  4. →IP address
  5. →Protocol
  6. →Authentication
  7. →Client
  8. →Packet
  9. →Port
  10. →Server
  11. →Session
  12. →TCP/IP
  13. →HTTP
  14. →Web application
  15. →CSRF token

Relationships

Sources & further reading

Where this data comes from

This entry was drafted by an AI from the sources above and has not yet been checked by a person. Treat it as a starting point, and check anything important against the sources.

See the review queueSuggest a correction on GitHubThis term as JSON

Check yourself

Loading…

Atlas is in beta.