Your App Got CSRF Protection in 2020 Without Doing Anything. Three Holes Remain.

Chrome made SameSite=Lax the default and most CSRF vulnerabilities quietly stopped working. That is real protection nobody implemented, and it leaves three specific gaps, one of which turns on the fact that SameSite was never about origins in the first place.

Tech Talk News Editorial8 min read
ShareXLinkedInRedditEmail
Your App Got CSRF Protection in 2020 Without Doing Anything. Three Holes Remain.

Key takeaways

  • Chrome made SameSite=Lax the default cookie behaviour in 2020, with Firefox and Edge following, which removed most classic CSRF attacks without application developers changing anything.
  • SameSite=Lax still sends cookies on top-level navigations using safe methods including GET, so any state-changing operation reachable by a GET request remains exploitable.
  • Research cited by practitioners finds at least 10.3% of web applications use GET-based state changes, which is the population for whom CSRF remains a live threat.
  • SameSite restricts by site rather than by origin, so a different subdomain of the same registrable domain is treated as same-site and its cookies are sent, making a subdomain takeover a CSRF vector.
  • Origin header validation is the only check that closes the same-site cross-origin gap, since the cookie itself will be sent regardless.
  • Browser coverage is not universal, because older, embedded and non-mainstream clients may treat cookies as though no SameSite attribute were set at all.

Something unusual happened to CSRF. Most vulnerability classes get fixed application by application. This one got substantially fixed for everyone at once, in 2020, when Chrome made SameSite=Lax the default cookie behaviour and Firefox and Edge followed.[1]

The effect is that the classic attack, a hidden form on an attacker's page that POSTs to your app with the victim's session cookie attached, stopped working. Most applications acquired protection they never implemented and in many cases do not know they have.

It is real protection and it is not complete. Three gaps remain, and the third is the one worth reading carefully, because it rests on a distinction the name actively obscures.

2020
When Chrome made Lax the default
GET
Method Lax still permits cross-site
10.3%
Applications with GET-based state changes
Origin
The only check that closes the subdomain gap

Gap One: State-Changing GETs

Lax is a compromise rather than a prohibition. It still sends cookies on top-level navigations using safe methods, which includes GET.[1] That is deliberate: it is what keeps you logged in when you follow a link to a site from somewhere else.

So the protection is method-shaped. If any endpoint changes state in response to a GET, an attacker needs only to get a victim to visit a URL, and the cookie goes along.[1]

The reflex is that nobody writes state-changing GETs. Practitioners citing research on this put the figure at at least 10.3% of web applications.[2] They exist as logout links, unsubscribe endpoints, one-click actions in emails, admin shortcuts, and anything built before the convention hardened. One in ten is not an edge case.

The browser did not decide GET is safe. It decided you follow the convention that GET is safe, and one application in ten does not.

Gap Two: Not Every Client Enforces It

Browser coverage is not universal. Older browsers, embedded webviews and non-mainstream clients may treat a cookie as though no SameSite attribute were set at all.[1]

Which matters more than the raw share of traffic suggests, because defences that depend on the client are defences your attacker gets to choose whether to be subject to. If an exploit only needs the victim to be in an in-app browser inside some other application, the population that enforces Lax is not the population that matters.

Gap Three: Same-Site Is Not Same-Origin

This is the subtle one, and the naming is genuinely unhelpful.

Same-origin means identical scheme, host and port. Same-site only requires the same registrable domain. So app.example.com and anything.example.com are same-site, and a cookie scoped to the parent domain is sent on requests originating from either.

The consequence: an attacker who controls anysubdomain can mount CSRF against your main application, and SameSite will not interfere, because from the cookie's point of view nothing cross-site is happening.

And subdomains get controlled more often than people expect. A forgotten DNS record pointing at a deprovisioned service, a marketing page on a third-party platform, a staging host nobody owns any more. Subdomain takeover is a routine finding, and it converts directly into a CSRF primitive here.

Why this matters

The only check that closes this gap is Origin header validation.[2] The cookie will be sent regardless, so the cookie attribute cannot help you; what distinguishes a request from a sibling subdomain is the Origin value. Comparing it against an expected value on state-changing requests, and rejecting a mismatch, is a few lines and catches precisely what SameSite cannot see.

What To Actually Do

Set SameSite explicitly rather than relying on the default. Use Lax for session cookies, or Strict where the user experience tolerates it. Relying on a browser default means relying on clients you do not control, and the attribute is one line.

Audit for state-changing GETs. This is the highest-value item and it is a grep rather than a project. Anything that mutates in response to GET should be a POST, or must carry a token.

Validate Origin on state-changing requests. Cheap, and the only thing that addresses the subdomain case.

Keep tokens for high-value operations.[2] Not because SameSite is worthless, but because the three controls fail in different circumstances: tokens hold regardless of browser behaviour, SameSite needs no application logic, and Origin validation catches the same-site cross-origin case neither of the others sees. That is the actual argument for layering, as opposed to layering because it sounds prudent.

Know which cookies you are protecting. All of this assumes a cookie-borne session, which is the right default for a same-origin app and is the argument in JWTs versus sessions. A token in localStorage is not exposed to CSRF at all, having traded that exposure for a worse one to cross-site scripting, since JavaScript can read it.

Takeaway

Default SameSite=Lax removed most of CSRF for most applications without anybody writing code, which is a genuinely unusual outcome and worth knowing you benefit from. It leaves state-changing GETs exploitable, which is roughly one application in ten, does not bind clients that ignore the attribute, and does not distinguish origins within a site, so any subdomain an attacker controls is a CSRF vector. Set the attribute explicitly, remove state-changing GETs, validate Origin, and keep tokens where the operation matters.

Sources and further reading

  1. 1.Reporting"Cross-site request forgery (CSRF): 2026 practitioner guide". Source for Chrome defaulting to SameSite=Lax in 2020 with Firefox and Edge following, for Lax still permitting cookies on top-level safe-method navigations, and for browser coverage being incomplete on older and embedded clients.
  2. 2.PrimaryOWASP, "Cross-Site Request Forgery Prevention Cheat Sheet". Source for SameSite being defence-in-depth rather than a replacement, for Origin header validation closing the same-site cross-origin gap, for anti-CSRF tokens on high-value operations, and for the finding that at least 10.3% of applications use GET-based state changes.
  3. 3.ReportingPortSwigger, "CSRF: bypassing SameSite cookie restrictions". Practical demonstrations of the bypass classes, including the same-site cross-origin case and state-changing GETs.

Frequently asked questions

Is CSRF still a problem in 2026?
Much less than it was, because Chrome made SameSite=Lax the default in 2020 and other major browsers followed, which stops the classic cross-site form post. It is not solved, since Lax still permits cookies on top-level GET navigations and treats subdomains as same-site, so applications with state-changing GETs or an untrusted subdomain remain exposed.
Does SameSite=Lax replace CSRF tokens?
Not in most deployments. It is a strong defence-in-depth control rather than a complete defence, because it does not cover state-changing GET requests, does not distinguish origins within the same site, and is not enforced by every client that will ever reach your application.
Why are GET requests still vulnerable to CSRF?
Because SameSite=Lax deliberately allows cookies on top-level navigations that use safe methods, so that following a link from another site keeps you logged in. If any endpoint changes state in response to a GET, an attacker can trigger it simply by getting a victim to visit a URL, and the cookie will be attached.
Is same-site the same as same-origin?
No, and the difference is the subtlest gap here. Same-origin means identical scheme, host and port, while same-site only requires the same registrable domain, so app.example.com and anything.example.com are same-site. A cookie set for the parent domain will be sent on requests originating from a subdomain, which means an attacker who controls any subdomain can mount CSRF against the main application.
How do you close the subdomain CSRF gap?
By validating the Origin header on state-changing requests, since that is the only signal that distinguishes a request from a sibling subdomain, and the cookie will be sent either way. Comparing the Origin against an expected value and rejecting a mismatch is cheap and catches exactly what SameSite cannot see.
Should I still use CSRF tokens?
Yes for high-value state-changing operations, alongside SameSite and Origin validation rather than instead of them. The three controls fail in different circumstances, which is the argument for having more than one: tokens work regardless of browser behaviour, SameSite works without any application logic, and Origin checks catch the same-site cross-origin case neither of the others addresses.

Written by

Tech Talk News Editorial

Computer engineering background. Writes about software, AI, markets, and real estate, and the places where the three meet.

More about the author
ShareXLinkedInRedditEmail