CSRF in 2025: Defeating SameSite With Method and Gadget Tricks
SameSite cookies blunted classic CSRF but did not end it. We cover detecting forgeable state-changing requests and the GET-method, lax-window, and same-site gadget bypasses with working PoCs.
Cross-site request forgery rides a victim's authenticated cookies to perform a state-changing action they never intended. Browser-default SameSite=Lax cookies killed the easiest cross-site POST, but CSRF is alive wherever a request changes state without an unguessable token — and several bypasses defeat SameSite itself.
Where it hides
The vulnerable shape is a state-changing endpoint authenticated only by an ambient cookie, with no per-request anti-CSRF token (or one that is not actually validated). Hunt for:
- Account mutations: change email, change password, add a recovery address, link an OAuth account.
- Money/role actions: transfer, purchase, invite admin, change permissions.
- Settings toggles and "delete account" that accept a simple form post.
- Endpoints that accept the request as a top-level form (
application/x-www-form-urlencoded) rather than requiringapplication/json+ a token.
The detection method is to take a legitimate state-changing request and strip it down: remove the Referer, remove any CSRF token, and see if it still succeeds. If the action goes through with only the cookie, it is forgeable:
POST /api/account/email HTTP/1.1
Host: app.example.com
Cookie: session=...
Content-Type: application/x-www-form-urlencoded
email=attacker@evil.com
If that 200s without a token, you have CSRF. Then check the cookie's SameSite attribute (read it from Set-Cookie or DevTools) because it dictates which bypass you need. Burp's CSRF PoC generator builds the auto-submitting form once you confirm the request.
Reproducing it
For a cookie with no SameSite attribute (treated as None by some older clients) or an endpoint reachable cross-site, the PoC is an auto-submitting form hosted on your page:
<form id="x" action="https://app.example.com/api/account/email" method="POST">
<input name="email" value="attacker@evil.com">
</form>
<script>document.getElementById('x').submit();</script>
When a logged-in victim loads your page, their browser posts the form with their session cookie attached, and the email changes. The mutated account state is the proof.
Now the SameSite=Lax bypasses, which are the interesting part:
GET-method state change. Lax cookies are sent on top-level cross-site GET navigations. If the same action is reachable via GET, a bare link or image fires it:
<img src="https://app.example.com/api/account/email?email=attacker@evil.com">
A top-level navigation works too: window.location='https://app.example.com/...' carries the Lax cookie.
The two-minute Lax window. Some browsers historically did not enforce Lax on a cookie for the first ~120 seconds after it is set. If the victim just authenticated, a cross-site POST within that window still carries the cookie — time the PoC to fire right after a login redirect.
Same-site gadget. SameSite is same-site, not same-origin, so a request from any subdomain counts as same-site. An open redirect, an XSS, or an HTML-injection gadget on any *.example.com host can issue the forged request with cookies attached. Chain a low-value sibling subdomain to reach the protected endpoint:
<!-- hosted on, or reflected through, sub.example.com (same site) -->
<form action="https://app.example.com/api/account/email" method="POST">
<input name="email" value="attacker@evil.com"></form>
<script>document.forms[0].submit()</script>
Going further
The impact is whatever the forged action does, so aim the PoC at takeover-grade endpoints:
- Change the account email, then trigger a password reset to that address — full takeover from a single click.
- Add an attacker passkey/OAuth identity or a recovery phone.
- Disable a security setting, or escalate a role you are about to control.
A reliable recon habit is to replay every state-changing request with the token and Referer removed and simply note which still succeed — those are forgeable regardless of how the UI looks. Then read each relevant cookie's SameSite to choose between a plain form, a GET gadget, and a same-site subdomain chain. Capture the forged request, the hosted PoC, and the resulting state change as evidence. Use accounts you control as the victim, on authorized scope only.