CSRF tokens: why your form needs a secret the attacker can't read
The attack you're defending against
You're logged into bank.com. An attacker tricks you into visiting evil.com. evil.com ships HTML like:
<form action="https://bank.com/transfer" method="post"> <input name="to" value="attacker" /> <input name="amount" value="9999" /> </form> <script>document.forms[0].submit()</script>
Your browser cheerfully attaches your bank.com cookies (you're logged in!) and POSTs the transfer. That's CSRF β Cross-Site Request Forgery.
The fix: server-rendered secret in the form
The server puts a random token into your form when it renders. The token is tied to your session, stored server-side, and is NOT readable from other origins (different origin = can't read the HTML the server sent you).
<form action="/transfer" method="post"> <input type="hidden" name="_csrf" value="a7f3...c4b2" /> <input name="to" /> <input name="amount" /> </form>
The server checks: "does _csrf match what I gave this session?" If not β 403. The attacker on evil.com can't read your CSRF cookie via JavaScript (different origin), can't read the rendered HTML (different origin), and can't guess the random token. Forgery blocked.
Three places people get this wrong
- One global token reused per session β works but leaks one token across many forms; safer is per-form rotation.
- Token in URL β leaks via Referer header, browser history, server logs. Always put it in a hidden form field, never a query string.
- No token on POST that "feels safe" β newsletter sign-up, "follow user" buttons. ANY state-changing POST needs a token.
Sign up to start coding
Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β card required, cancel anytime.
Sign up β free trial βFirst 10 lessons in each track are free. No card needed for those.