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:
1<form action="https://yourbank.com/transfer" method="POST" id="f">
2 <input type="hidden" name="to" value="attacker-account" />
3 <input type="hidden" name="amount" value="5000" />
4</form>
5<script>document.getElementById("f").submit();</script>The victim's browser sends the request to yourbank.comwith 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
1app.get("/transfer", (req, res) => {
2 const csrfToken = generateCsrfToken(req.session);
3 res.render("transfer-form", { csrfToken });
4});1<form action="/transfer" method="POST">
2 <input type="hidden" name="csrf_token" value="{{csrfToken}}" />
3 <!-- other fields -->
4</form>2. Reject the request if the token is missing or wrong
1app.post("/transfer", (req, res) => {
2 if (!validateCsrfToken(req.session, req.body.csrf_token)) {
3 return res.status(403).send("Invalid CSRF token");
4 }
5 // process the transfer
6});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 - 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=Laxon 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.