If a logged-in user visits a malicious page while their session cookie is still active, that page can silently submit a form to your site on their behalf - and the browser happily attaches their real session cookie to it. A CSRF token is what tells your server the request didn't actually come from your own form.
For how the attack works in the first place, see what is a CSRF attack. This guide covers the fix.
How the Attack Actually Works
The attacker doesn't need to steal a session cookie or bypass authentication - they just need the victim's browser to still be logged in, and to visit a page they control:
The victim's browser sends the request to yourbank.com with their real session cookie attached - because that's just how cookies work, regardless of which page initiated the request. Without a CSRF token, the server has no way to tell this apart from a legitimate submission.
How to Fix It
1. Generate a per-session (or per-form) token
2. Reject the request if the token is missing or wrong
3. Use your framework's built-in CSRF middleware where available
Most mature web frameworks (Django, Rails, Laravel, and Express via csurf or similar) ship CSRF protection you can enable rather than implement by hand - see our API security basics guide for the broader pattern - reach for that first before rolling your own token generation and comparison.
4. Pair it with SameSite cookies, don't rely on either alone
SameSite=Lax on the session cookie and a CSRF token on state-changing forms are complementary, not redundant - each closes a gap the other doesn't fully cover.
How Scanverra Detects This
Scanverra's security scan submits test requests to POST forms it finds on your site and flags any that process the request without requiring a CSRF token, so you know exactly which forms are exposed rather than having to audit every one by hand.