Skip to content

CSRF token

A unique secret value embedded in HTML forms (typically as a hidden input) that the server validates on POST. Defends against Cross-Site Request Forgery -- an attacker can't submit a form from another origin without knowing the token.

A CSRF (Cross-Site Request Forgery) token is the server-side defense against an attacker tricking a logged-in user into performing actions they didn't intend. The classic attack: malicious site hosts an <img src="https://your-bank.com/transfer?to=attacker&amount=1000"> -- the user's browser dutifully sends the cookie, the bank thinks the user authorized the transfer.

The defense: every state-changing form on your site includes a hidden input with a random per-session value:

<form method="post" action="/transfer">
  <input type="hidden" name="_csrf" value="9d8a4f7c-...">
  <input name="to"> <input name="amount">
  <button>Transfer</button>
</form>

The server validates the token on POST. An attacker on another origin can't read the token (same-origin policy blocks it), so they can't forge a valid form submission.

Framework conventions for the hidden input name:

  • Django: csrfmiddlewaretoken
  • Rails: authenticity_token
  • Laravel: _token
  • ASP.NET: __RequestVerificationToken
  • Express + csurf: _csrf
  • Generic: csrf_token, xsrf-token

Modern alternatives:

  • SameSite cookies (Strict / Lax) — the browser refuses to send the session cookie on cross-origin POSTs, so the request fails authentication regardless. Modern browsers default to Lax, which covers most CSRF attacks.
  • Double-submit cookie — server sends a CSRF value as both a cookie AND a custom header; client JS reads the cookie and echoes it in the header on each request. Requires no per-form state.
  • Custom request headers — APIs that require X-Requested-With: XMLHttpRequest (or any custom header) can't be triggered cross-origin without CORS preflight, which the server controls.

Best practice: belt + suspenders. Use SameSite=Lax (or Strict) AND include CSRF tokens in forms. The token catches the residual cross-origin attacks SameSite doesn't (subdomains, browsers without SameSite default).

Related terms

Further reading

Send Feedback