Server-side safety
Protect cookie-authenticated forms from CSRF
Understand cross-site request forgery and combine SameSite cookies, CSRF tokens, and origin checks according to the application’s needs.
Cross-site request forgery, or CSRF, is an attack where another site makes your visitor’s browser send a request to your server, and the browser attaches the visitor’s cookies to it. The request arrives authenticated. The person never meant to send it.
How it happens
You are signed in to bank.test. Your session lives in a cookie. You open a different site, and that site contains this:
<form action="https://bank.test/transfers" method="post">
<input name="to" type="hidden" value="attacker-account">
<input name="amount" type="hidden" value="5000">
</form>
<script>document.forms[0].submit()</script>
The form submits on its own. The browser sees a request to bank.test and attaches the bank.test cookies, because that’s what cookies do. Your server sees a valid session and a valid transfer form. Nothing says “this came from somewhere else”.
Start with the HTTP contract
Before any token, get the basics right:
- GET and other safe requests must never change data. If a GET deletes a record, an
<img src>on any site can trigger it. - State-changing requests use POST or another appropriate method.
- Session cookies use a suitable
SameSitepolicy. - Sensitive endpoints get a CSRF defense that fits your architecture.
The CSRF token
The classic defense for server-rendered forms is a random token tied to the session, placed in the form as a hidden field:
<form action="/account/email" method="post">
<input name="csrfToken" type="hidden" value="random-session-token">
<!-- email field and submit button -->
</form>
When the form comes back, the server compares the submitted token with the one it expects for this session. No match, no change.
Why does this work? The attacker can make the browser submit a form to your site. They can’t read a page from your site, because the browser blocks cross-origin reads. So they never learn the token.
The token must be unpredictable, checked on every state-changing request, and kept out of logs and URLs. It only has to be secret from other origins, not from the person using the page.
Origin checks
Browsers send an Origin header on cross-site POST requests. Checking that it matches your own origin adds a second layer. Some setups fall back to Referer when Origin is missing. Either way, don’t accept a missing or mismatched origin on a sensitive endpoint.
SameSite is not the whole answer
SameSite=Lax, the default in modern browsers, already blocks cookies on cross-site POST requests. But cross-origin features, older clients, and subdomains complicate the picture. Treat it as one layer, not a reason to skip the others.
What CSRF protection doesn’t cover
A script injected into your own origin through cross-site scripting can read the token and submit the form with it. CSRF defenses assume your origin isn’t compromised. XSS is a separate problem with a separate fix.
Try this: post to a protected endpoint with no token, with a wrong token, and with the right one. Only the last request should change anything.
Lesson completed