The 2 minutes where SameSite=Lax doesn't stop CSRF

For the first 120 seconds after it's set, Chrome still sends a cookie with no explicit SameSite attribute on a cross-site POST request. That's a deliberate carve-out from Chrome's own Lax default, built in to keep SSO flows from breaking. This post walks through how the carve-out works, why the window stays open longer than "just after login," and how to actually shut it.

SameSite=Lax in one sentence

Since Chrome 80 (February 2020), every cookie without an explicit SameSite attribute automatically gets SameSite=Lax. Such a cookie only rides along on requests from your own site, plus on "safe" top-level navigation from another site, like a user clicking a link. On a cross-site POST the cookie stays behind, and that's no accident. An auto-submitting form on an attacker's site is cross-site by definition, which makes it the classic CSRF vector. So in theory, SameSite=Lax wipes out most CSRF without a developer lifting a finger.

That's the theory, anyway. In practice, Chrome built in an exception that makes the story a lot less black and white.

The exception nobody reads: Lax+POST

The official Chromium documentation is clear about it. A cookie that is at most 2 minutes old still goes out on a top-level cross-site POST request, even without an explicit SameSite attribute. Chromium calls this "Lax+POST" or "Lax+Unsafe" itself, and says plainly that it's a deliberate, temporary mitigation. Not a bug.

The reason is practical. Some single sign-on implementations finish a login with a cross-site POST redirect back to the site, right after the session cookie has been set. Enforce strict Lax rules there and you break that exact flow. So Chrome opted for a 120-second grace period rather than breaking all of them outright.

Here's the detail that gets missed most often, and the single most important point in this article. This exception applies only to cookies that don't set a SameSite attribute. Write SameSite=Lax yourself in the Set-Cookie header and you get the normal Lax rules, with the 2-minute grace period gone entirely. We'll come back to that at the fix.

Why this is a backdoor for CSRF

The entire point of SameSite=Lax as a CSRF mitigation is that a cross-site POST doesn't get the cookie. Lax+POST props that door back open for a little while. The attack recipe is short:

  • The target uses a session cookie with no explicit SameSite attribute. That's the default, and still very common.
  • A state-changing endpoint is reachable over POST with no anti-CSRF token. Think "change email address" or "reset password."
  • The victim received that cookie less than 2 minutes ago.

Point three is where most people write the attack off. The attacker would have to strike within 2 minutes of a login, which sounds impractical. And it is, as long as they're waiting on coincidence. But coincidence isn't the only way in.

The trick that stretches the window: cookie refresh

Those 2 minutes don't run from the very first time the cookie was ever set. They run from the most recent moment the server sent a Set-Cookie for it. And if the server hands out a fresh session cookie every time someone completes a login, even when they were already logged in, an attacker can trigger that moment themselves and open a brand-new 2-minute window exactly when it suits them.

And this isn't hypothetical. PortSwigger's Web Security Academy has a whole lab for it, "SameSite Lax bypass via cookie refresh." It runs like this. A site with OAuth login sets a new session cookie on every completed OAuth flow, even when the user already had a valid session. The attacker pushes the victim's browser through a quiet top-level navigation past /social-login, say an auto-redirecting popup or window.open. That finishes the OAuth flow in the background, the server sets a fresh cookie, and the 2-minute clock restarts for a session that was already there. A few seconds later a hidden form submits the real CSRF payload, a changed email address for instance, and the fresh cookie rides along with it.

That changes the whole picture. The vulnerable window is no longer "the first 2 minutes after login." It's "the first 2 minutes after any moment the attacker can force a repeated cookie issuance." And on sites that refresh the session cookie on every re-authentication, or even on every request through rolling or sliding sessions, that window can stay open almost continuously for an actively logged-in user.

This isn't a 0-day

None of this is an unknown vulnerability in Chrome. It's named and explained in the official Chromium documentation, and PortSwigger has covered the cookie-refresh technique in the Web Security Academy for years. The problem was never secrecy. It's that many developers treat implicit SameSite=Lax (via the browser default) as if it meant "a cross-site POST is always blocked."

Could this kind of subtle CSRF gap be in your application too? A manual penetration test looks precisely for the logic flaws that automated scanners miss.

Schedule a free intake →

How to close this in your own application

Always set SameSite explicitly

The simplest fix is also the most concrete. Write SameSite=Lax (or Strict, where you can) yourself in the Set-Cookie header instead of leaving it to the browser default. The Lax+POST grace period only applies to cookies without an explicit attribute, after all. One extra word in your response header, and the entire attack scenario in this article stops applying.

Set-Cookie: session=abc123; Path=/; HttpOnly; Secure
   (implicit Lax, exposed to the 2-minute window)

Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
   (explicit, no Lax+POST exception)

Don't rely on SameSite as your only CSRF defence

SameSite is a strong layer of defence, and it's free, but it's still just a layer. Not a replacement for anti-CSRF tokens on state-changing endpoints. For sensitive actions like changing an email, resetting a password, payments, or account settings, an unpredictable token or a double-submit cookie pattern is still the real guarantee. SameSite catches what slips through; it doesn't take over the check.

Audit endpoints that quietly refresh a session cookie

Walk every endpoint that sends a Set-Cookie for your session without the user consciously logging in again. Think SSO callbacks, "remember me" refresh, or rolling-session middleware that re-sets the cookie on every request. Can any of them be reached through a plain top-level navigation, a link, a redirect, or a window.open, without the user doing a thing? Then you've found the gadget the cookie-refresh technique is looking for.

For SSO/OAuth flows: choose explicitly, don't rely on grace periods

Does your own login flow need a cross-site POST redirect to come through with the session intact? Then set SameSite=None; Secure deliberately on that one cookie, instead of leaning on a grace-period rule that Chrome itself has called "temporary" for years and could pull at any point.

Conclusion

SameSite=Lax is a good default. It takes a large share of CSRF off the table without anyone having to do anything. But "a large share" isn't "all of it," and certainly not "from second one." Lax+POST is a deliberate, documented exception, and the cookie-refresh technique shows the window it opens is often wider than "the first 2 minutes after login."

In most stacks the fix costs one line. Write SameSite explicitly instead of letting the browser default decide, and treat it as a complement to anti-CSRF tokens, not a replacement.

Is your application quietly relying on browser defaults?

SameSite, CORS, cookie flags. Exactly the kind of configuration a manual pentest checks and an automated scan skips right past.

Go to web application pentest →