CSRF
A complete guide to understanding, detecting, exploiting, and preventing CSRF vulnerabilities.
Introduction
Cross-Site Request Forgery (CSRF)
ANAS EDUCATION -- Bug Bounty & Pentesting Course (V2 Beginner-First)
SECTION 1. Introduction
Imagine you open your PC and visit `anasbank.com`.
You log in. Your browser receives a cookie:
From this moment until you log out, every request your browser sends to `anasbank.com` automatically carries that cookie. This is how the browser tells the server "this is the same user as a minute ago". You do not type your password again. The cookie speaks for you.
You navigate to the transfer page. You fill the form: amount 100 dirhams, destination account `ANAS-987654`. You click Submit. The browser sends:
The server checks the cookie. You are logged in. It processes the transfer. Normal. Expected. The user authenticated, the user filled the form, the user clicked submit.
Now keep that tab open and, in a second tab, visit a completely different website, `cute-puppies.com`. You did not log out of the bank; you never do. The puppies site is just a page about dogs. You scroll. You enjoy.
That puppies page contains, hidden somewhere on it, this HTML:
The puppies page just told your browser: "send this form to `anasbank.com/api/transfer`".
Your browser sends the POST. Because the request goes to `anasbank.com`, the browser automatically attaches the cookie for `anasbank.com`. The browser does not care that the request was triggered by code on `cute-puppies.com`; cookies travel with the destination, not the source. The bank receives the request, sees the valid session cookie, and processes the transfer.
10,000 dirhams just left your account. You did not type anything. You did not click anything dangerous. You looked at puppies.
This is Cross-Site Request Forgery, CSRF for short. It is one of the oldest, most fundamental, and most still-exploitable web vulnerabilities in 2026. The bug does not live in the bank's code logic. The bug lives in the bank's implicit assumption that "if my cookie arrived, the request was made on my page".
This course teaches the CSRF bug class from zero. By the end you will know:
- ●Why browsers attach cookies automatically and why that is the entire root cause.
- ●The difference between CSRF, CORS, XSS, and Clickjacking (often confused).
- ●Classic POST, GET-based, JSON, and login CSRF.
- ●SameSite cookie bypasses, Referer regex bypasses, content-type bypasses.
- ●How to detect, exploit, and prevent each variant with proper defense in depth.
You do not need to be an expert in HTTP. You need to understand that a cookie is a passport and the browser hands it to anyone who asks the right destination.
SECTION 2. How It Works
To find these bugs you first need to understand exactly what the browser does with cookies, what the server sees, and where the implicit trust hides.
Step 1. What a cookie is
A cookie is a small named string the server tells the browser to remember. The server sends `Set-Cookie` on the login response; the browser stores it; the browser attaches it on subsequent requests to that domain.
- ●`Domain=anasbank.com` ==> only requests to anasbank.com (and possibly subdomains) carry this cookie.
- ●`Path=/` ==> the cookie is sent for any path on that domain.
- ●`Secure` ==> only over HTTPS.
- ●`HttpOnly` ==> JavaScript cannot read the cookie via `document.cookie`.
- ●Missing here but critical: `SameSite` ==> controls whether the cookie travels on cross-site requests.
Step 2. Cookies are attached by destination, not by origin
This is the central fact. When `cute-puppies.com` (or any page) causes the browser to send a request to `anasbank.com`, the browser asks one question: "do I have any cookies stored for anasbank.com?" If yes, attach them. The browser does not check who initiated the request.
Step 3. The two conditions for CSRF
Every CSRF attack needs exactly these two conditions:
- ●1. The victim is currently authenticated to the target site (a session cookie exists in the browser).
- ●2. The attacker can cause the victim's browser to send a request to the target site (visit a page, fetch an image, click a link, embed an iframe, anything that emits HTTP).
Both are easy. Most people stay logged in to their email, social media, bank, work tools for hours or days. Causing the browser to emit an HTTP request takes a single HTML tag.
Step 4. The five primitives that fire requests
A page on the attacker's domain has five common ways to make the victim's browser hit a target URL:
- ●`<form action=... method=POST>` with `<script>document.forms[0].submit()</script>` ==> classic POST.
- ●`<img src="https://target/...">` ==> GET request.
- ●`<iframe src="https://target/...">` ==> GET request.
- ●`fetch(..., { credentials: 'include' })` ==> POST/PUT/DELETE with cookies, with some CORS-imposed limits.
- ●`<a href=...>` with `<script>document.querySelector('a').click()</script>` ==> top-level GET.
Each of these triggers a request that carries the cookie for the destination domain. None of them require the victim to type or click on anything malicious.
Step 5. Why authentication is not the same as authorization
The server's logic looks like:
`session['user_id']` is the authentication check. It tells the server "this request has a valid session". It does NOT tell the server "the user just intentionally pressed Submit on anasbank.com's transfer form". The two are not the same. CSRF lives in the gap between them.
Step 6. The safe flow
The request originates from anasbank.com itself; the bank can verify this with `Referer`/`Origin` headers, a CSRF token in the form, a custom header, or all three.
Step 7. The vulnerable flow
The bug is the bank trusting "cookie present" as proof of intent.
Step 8. What CSRF is not
- ●Not phishing. The victim never types credentials into a fake page.
- ●Not XSS. No JavaScript runs on the target domain.
- ●Not Clickjacking. The victim does not click anything.
- ●Not stolen credentials. The attacker never sees the password or token.
- ●Not a server compromise. The server is responding correctly to a forged request.
CSRF is the request being made on the victim's behalf without their knowledge or consent.
SECTION 3. Attack Flow
The walkthrough below is the canonical CSRF attack against an email-change endpoint, which is the gateway to full account takeover.
Step 1: Recon
Map every state-changing endpoint the victim can reach when authenticated. Common candidates: change email, change password, disable 2FA, transfer money, delete account, delete content, post on behalf of user, follow/unfollow, approve OAuth.
Step 2: Capture the request
In Burp Proxy (or browser DevTools), log in as a test user and perform the action once. Note the full request: method, URL, headers, body, cookies, content type.
Step 3: Look for defenses
Check the request and response for:
- ●A CSRF token in the form body, URL, or as a header (`csrf_token`, `_csrf`, `authenticity_token`, `X-CSRF-Token`).
- ●Session cookie attributes (`SameSite=Strict`, `Lax`, or none).
- ●Origin/Referer validation (replay the request from a different origin, see if the server still accepts it).
- ●Content-Type restrictions (try changing `application/x-www-form-urlencoded` to `text/plain` to `application/json`).
- ●A custom header like `X-Requested-With: XMLHttpRequest`.
If none of these defenses exist or any can be bypassed, the endpoint is CSRF-vulnerable.
Step 4: Build the attacker page
Create an HTML file that auto-submits the request:
Step 5: Test against a controlled session
Open the page in a browser where a test user is logged in to the target. Watch the action complete.
Step 6: Deliver
Host the page at an attacker-controlled URL. Deliver to the victim via phishing email, forum post, malvertising, link shortener, anywhere the victim might visit while logged in to the target.
Step 7: Capture impact
The action executes silently in the victim's session. If the action was email change, follow up with a password reset and take over the account. If it was money transfer, the funds are gone.
ASCII timing diagram
The whole sequence takes seconds. The victim is unaware throughout.
SECTION 4. Why Developers Make This Mistake
CSRF is not a code-quality bug. It is a model-of-the-browser bug.
Mistake 1: "If my server checks the session, the request is authorized"
Authentication and authorization-of-intent are different. The session proves identity; nothing in a plain POST proves the user intended the action.
Mistake 2: "Cross-origin requests are blocked by CORS"
CORS controls who can READ the response. It does not block sending requests. A `<form>` submission is a "simple" cross-origin request that fires without preflight and without CORS approval; the response is hidden from the attacker, but the side effect on the server already happened.
Mistake 3: "Cookies only travel on same-site requests"
Only true if the cookie is `SameSite=Strict` or `SameSite=Lax` with restrictions. The default for many older systems is no SameSite or `SameSite=None`. In those cases cookies travel on every request to their domain, including cross-site form submissions.
Mistake 4: "My API is on a different subdomain, so it is safe"
Subdomains share the parent domain's cookies if `Domain=.target.com` is set. Subdomain takeover plus shared cookies equals subdomain-launched CSRF against every other subdomain.
Mistake 5: "JSON APIs are safe because browsers preflight"
Only if the server validates the preflight is acceptable AND only if the request actually uses `application/json`. If the server accepts the same body with `Content-Type: text/plain`, there is no preflight, and JSON CSRF works.
Mistake 6: "We use bearer tokens, not cookies"
True for pure-token APIs. But many hybrid apps use cookies for the web UI and bearer tokens for mobile. The web UI endpoints are still CSRF-vulnerable.
Mistake 7: "The framework handles it"
Only if the developer left it on. `@csrf_exempt` in Django, `http.csrf().disable()` in Spring Security, missing `csurf` middleware in Express, missing `Flask-WTF` integration in Flask all silently remove the protection that the framework would otherwise provide.
SECTION 5. Beginner Summary
- ●CSRF is when an attacker tricks a victim's browser into sending an authenticated request to a target site without the victim's knowledge.
- ●The browser attaches the session cookie automatically because the request goes to the target's domain. The server cannot tell the request was initiated cross-site.
- ●CSRF tokens (random per-session secret in the form), `SameSite` cookies (block cross-site cookie attachment), Origin/Referer validation, and custom headers all stop classic CSRF when applied together.
- ●The victim does not click anything dangerous and does not enter credentials. Just visiting an attacker page is enough.
- ●CWE-352 is the canonical mapping. OWASP historically classified CSRF in its own A8:2013 slot; current Top 10 maps it under A01:2021 Broken Access Control.
SECTION 6. Visual Explanation
Where cookies are attached
The destination determines which cookies travel; the initiator does not.
Four families of CSRF
Defense stack (defense in depth)
CSRF vs CORS vs Clickjacking vs XSS at a glance
CSRF is the only one of these where the attacker neither runs code on the target nor needs the victim to click anything.
SECTION 7. Definition
Technical definition
Cross-Site Request Forgery (CSRF, sometimes XSRF) is a vulnerability in which an attacker causes an authenticated victim's browser to send an unintended state-changing HTTP request to a target application, exploiting the browser's automatic transmission of credentials (session cookies, HTTP Basic auth headers, NTLM tokens, client certificates) to make the request indistinguishable from a legitimate user action.
- ●CWE-352: Cross-Site Request Forgery (CSRF)
- ●OWASP Top 10 (2017): A8 Cross-Site Request Forgery (had its own slot)
- ●OWASP Top 10 (2021): merged under A01 Broken Access Control
- ●Historical alternate name: XSRF, Session Riding, One-Click Attack
Beginner-friendly definition
CSRF is when the attacker uses your already-logged-in browser to do something on a website you trust, without your knowledge, by making your browser send a request you did not intend.
Why it matters
CSRF remains a live, high-impact bug class in 2026. Real disclosed bounty examples:
- ●Dropbox -- Exfiltrate Google Drive access token using CSRF, paid $1,728.
Listed in https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md
- ●Internet Bug Bounty -- Argo CD CSRF leads to Kubernetes cluster compromise, paid $4,660.
Listed in the top corpus above.
- ●Apache Airflow -- CVE-2023-49920: missing CSRF protection on DAG/trigger, Internet Bug Bounty disclosure.
- ●TikTok Ads Portal -- CSRF, paid $1,000.
- ●HackerOne itself -- HackerOne reports escalation to JIRA is CSRF vulnerable, paid $500.
- ●Slack -- CSRF in GitHub integration, paid $500.
- ●Shopify -- H1514 CSRF in domain transfer allows adding your domain to other user's account.
- ●Shopify -- Wholesale CSRF to generate invitation token for a customer.
- ●Mail.ru -- Disable 2FA via CSRF (leads to 2FA bypass).
- ●Mozilla -- CSRF to information disclosure on password reset.
- ●IBM -- POST-based CSRF leading to modification of contact information.
- ●X (Twitter) -- CSRF on https://www.niche.co leads to "account disconnection".
The pattern is consistent: any state-changing endpoint without a properly validated CSRF defense remains exploitable in 2026.
Common affected systems
- ●Account settings: email, password, phone, security questions, 2FA
- ●Banking and payment endpoints: transfer, withdraw, change destination
- ●Admin panels: delete user, ban user, modify roles, change configs
- ●Social features: post, comment, like, follow, send DM
- ●E-commerce: add to cart, checkout, change shipping address, request refund
- ●OAuth and SSO consent and revocation endpoints
- ●Webhook configuration endpoints (modify webhook URL = data exfiltration)
- ●Integration management (connect/disconnect third-party services)
- ●CMS publish/unpublish/delete content endpoints
- ●IoT and home automation control endpoints
If a state change is made via a cookie-authenticated session, CSRF is a candidate vulnerability.
SECTION 8. Examples
Five realistic AnasTech scenarios. Each one matches the shape of a real disclosed bounty report.
Example 1. AnasBank money transfer (classic POST CSRF)
The feature. The transfer page on `anasbank.anastech.com` submits:
The bug. No CSRF token. Session cookie has no `SameSite`. No Origin or Referer validation.
The attack step by step.
- ●Step 1: build a small HTML page with an auto-submit form pointing at the transfer endpoint, with `to=ATTACKER&amount=10000` hardcoded.
- ●Step 2: host on attacker.com.
- ●Step 3: send the link to victims who are logged in to anasbank.
- ●Step 4: visitor's browser auto-submits; transfer executes.
Example 2. AnasMarket email change leading to ATO (account takeover)
The feature. The profile page changes email via:
The bug. No CSRF defense. The change does not require old-password confirmation.
The attack step by step.
- ●Step 1: PoC HTML auto-submits the form with `email=attacker@evil.local`.
- ●Step 2: deliver to victim via phishing or forum link.
- ●Step 3: email changes silently in the victim's session.
- ●Step 4: attacker visits `/forgot-password`, requests reset, receives the reset email at their address.
- ●Step 5: attacker sets a new password and logs in. Full ATO.
Example 3. AnasCorp admin GET-based zero-click deletion
The feature. The admin panel allows deletion via:
The endpoint changes state via GET, with no token, no SameSite.
The bug. State-changing GET is the worst CSRF anti-pattern. Any HTML page can fire it with one tag.
The attack step by step.
- ●Step 1: place `<img src="https://anascorp.anastech.com/admin/delete_car.php?id=42">` on any public page (an article, a forum, a comment).
- ●Step 2: any admin who views the page silently deletes car #42.
- ●Step 3: chain multiple `<img>` tags to delete many records at once.
This pattern matches the `<script>window.location.href = "http://target.local:63333/admin/delete_car.php?id=19";</script>` PoC shape that recurs in real penetration tests and bug bounty submissions.
Example 4. AnasOne JSON API content-type bypass
The feature. The mobile-focused JSON API exposes:
The application enforces CSRF only when `Content-Type` is `application/x-www-form-urlencoded` (the developer believed JSON requests cannot be CSRF-forged because they trigger CORS preflight).
The bug. A `<form>` cannot send `application/json`, but `fetch` from JavaScript can send `Content-Type: text/plain` with a JSON body. `text/plain` does NOT trigger preflight (it is a CORS "simple" content type). If the server parses the body as JSON regardless of the content-type header, the attack works.
The attack step by step.
- ●Step 1: build a page that calls `fetch('https://target/api/v2/profile', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'text/plain' }, body: JSON.stringify({email: 'attacker@evil.local'}) })`.
- ●Step 2: browser sends the request without preflight; cookies are attached because `credentials: 'include'` and the destination is target.com.
- ●Step 3: the server (if loose JSON parsing) updates the email.
Example 5. AnasSocial login CSRF
The feature. The login endpoint accepts:
No CSRF token on the login form.
The bug. Login CSRF lets the attacker force the victim's browser to log in to an attacker-controlled account. The victim is now operating in the attacker's account without realizing it. Subsequent typing (search queries, saved payment info, posts) is stored in the attacker's account.
The attack step by step.
- ●Step 1: attacker creates a real account on the platform.
- ●Step 2: PoC HTML submits the login form with attacker credentials.
- ●Step 3: victim visits the page; their browser logs them in to attacker's account.
- ●Step 4: victim now innocently enters a credit card during checkout, which lands in attacker's account.
Underused historically but devastating against fintech, e-commerce, and ad-buying platforms.
SECTION 9. Vulnerable Code
Below are the same shape of bug in different languages and frameworks. The flaw is structural: the server confirms the session and acts, with no confirmation that the request was initiated from the application's own UI.
Python (Flask)
Python (Django) -- regression by `@csrf_exempt`
PHP
Node.js (Express)
Java (Spring Security) -- explicit regression
Ruby on Rails -- explicit regression
ASP.NET MVC -- explicit regression
GET-based state change (any language)
A single `<img src=...>` tag on any page triggers this.
The universal pattern across languages
- ●1. The server receives a state-changing request.
- ●2. The server checks the session cookie (authentication).
- ●3. The server processes the request without verifying it was initiated from its own UI.
- ●4. The server returns success.
Step 3 is where the bug lives. Every fix in section 16 adds the missing verification.
EOFSECTION_NEVER_USED
SECTION 10. Detection
CSRF detection is a request-replay exercise. You take a known-working request, strip out the defense (if any), replay it, and see if the server still accepts.
Manual workflow
- ●Step 1: log in as a test user; perform the sensitive action; capture the full request in Burp Proxy or DevTools.
- ●Step 2: catalog visible defenses in the request: any `csrf_token`/`_csrf`/`authenticity_token`/`X-CSRF-Token` field or header? Any custom header like `X-Requested-With`?
- ●Step 3: catalog cookie attributes: open Application/Storage tab in DevTools; for each cookie attached to the request, note `SameSite`, `Secure`, `HttpOnly`, `Domain`.
- ●Step 4: try replay variations in Burp Repeater and observe responses:
- ●Remove the CSRF token entirely.
- ●Empty the CSRF token (`csrf_token=`).
- ●Replace the CSRF token with one from a different session.
- ●Remove the `Origin` and `Referer` headers.
- ●Replace `Origin: https://target.com` with `Origin: https://attacker.com`.
- ●Change `Content-Type: application/x-www-form-urlencoded` to `text/plain` (and adjust body).
- ●Change `Content-Type: application/json` to `text/plain` (keep JSON body).
- ●Try a different HTTP method (POST -> GET, POST -> PUT) and look for permissive handling.
- ●Step 5: if any variation succeeds, build a PoC HTML page that performs the same request from a third-party origin and verify it works in a real browser.
Burp Suite step by step
- ●Right-click a captured request -> "Engagement tools" -> "Generate CSRF PoC". Burp produces an HTML page that auto-submits the same request.
- ●Save the HTML, host it on an attacker domain (or open from `file://` for local testing).
- ●Visit while a test user is logged in to the target.
- ●If the action executes, CSRF confirmed.
Automated tools
- ●OWASP ZAP -- built-in passive checks for missing tokens and active CSRF scan.
- ●Burp Suite Pro Active Scan -- flags missing-token and weak-validation cases.
- ●Nuclei templates -- token-absence detection: https://github.com/projectdiscovery/nuclei-templates
- ●Tuhin1729 CSRF methodology checklists: https://github.com/tuhin1729/Bug-Bounty-Methodology/blob/main/CSRF.md
One-shot probe with curl
If the response is 200 OK and the email actually changed, the endpoint accepts requests without CSRF defense.
Indicators of vulnerability
- ●Form/request body without any CSRF token field.
- ●AJAX request without a custom header (no `X-Requested-With`, no `X-CSRF-Token`).
- ●Session cookie missing `SameSite` attribute or `SameSite=None`.
- ●State-changing action accessible via GET (delete, update, transfer).
- ●Server accepts the request when `Origin` is replaced with attacker domain.
- ●Server accepts the request with `Content-Type: text/plain` despite the body being JSON.
- ●CSRF token validation that fails open (accepts blank or absent token).
- ●CSRF token that does not change between sessions, or whose value comes from a non-session cookie.
- ●No password re-authentication on high-value actions (transfer, change password, disable 2FA).
Build a checklist. For each state-changing endpoint, walk the list. The endpoints that fail multiple checks become PoC candidates.
SECTION 11. Exploitation
Exploit techniques range from a one-line PoC to multi-step chains with bypasses.
Workflow
- ●1. Identify a sensitive state-changing endpoint.
- ●2. Confirm absence of (or bypassable) CSRF defenses.
- ●3. Build the simplest PoC that works.
- ●4. Test against your own test session.
- ●5. Host on an attacker domain and deliver to a (consenting) test victim.
- ●6. Capture screenshots and HTTP traces showing the cookie, the request, and the resulting state change.
- ●7. Chain to highest impact: email change -> password reset -> ATO; transfer -> funds extraction; admin -> bulk deletion.
Techniques
1. Classic POST CSRF (auto-submit form)
The most reliable PoC. Works because `application/x-www-form-urlencoded` is a CORS "simple" content type and does not require preflight; cookies attach automatically because the destination matches.
2. GET-based CSRF (one image or one redirect)
When the endpoint accepts state-changing GET:
Or a top-level redirect:
The redirect form is particularly effective for `SameSite=Lax` cookies, which travel on top-level GET navigations.
3. Empty / missing token bypass
Some servers check `if (token) { validate(token) }` rather than `if (token === expected)`. Send the form with no token field at all, or with an empty value:
If the server returns success, the bypass works.
4. Token-from-different-session bypass
If the server validates only the format of the token but not its binding to the session, generate a token in your own session and supply it in the victim's request:
5. Token-in-non-session-cookie bypass (PortSwigger lab pattern)
Some applications store the CSRF token in a non-session cookie and validate request token == cookie token. If the attacker can set that cookie via response header injection, cache poisoning, or a sibling subdomain, both values become attacker-controlled.
6. Token duplicated in cookie (PortSwigger lab pattern)
Some apps just check that the token in the form equals the token in the cookie. If the attacker can set the cookie value (via subdomain, CRLF injection in a header, or by tricking the user into visiting a page that sets it), the attacker controls both sides of the comparison.
7. Token leakage via XSS chain
If the application has any XSS (even a self-XSS), use it to fetch a page same-origin, parse the CSRF token, then submit the forged request with the stolen token:
XSS + CSRF defeats any token-based defense.
8. Token leakage via CORS misconfiguration
If a token-issuing endpoint returns `Access-Control-Allow-Origin: <reflected-origin>` and `Access-Control-Allow-Credentials: true`, the attacker page can fetch the token cross-origin with credentials and submit the CSRF.
9. Referer header `meta` strip
If the server validates Referer only when present (`if (referer && !startsWith(referer, 'target.com'))`), stripping the header bypasses the check.
10. Referer regex bypasses
Naive checks like `referer.includes('target.com')` accept attacker-controlled URLs that contain the substring:
Any of these passes a substring check while the request originates from `attacker.com`.
11. SameSite=Lax bypass via top-level GET
`SameSite=Lax` cookies are sent on top-level GET navigations. If a state-changing endpoint accepts GET (or accepts both POST and GET), navigating the victim's browser to that URL carries the cookie:
12. SameSite=Lax POST 2-minute window (Chrome)
Chrome historically allowed `SameSite=Lax` cookies on cross-site POST navigations within ~2 minutes of cookie creation (the "Lax + POST" mitigation window). If the victim recently logged in, this short window may permit cross-site POST with cookies attached.
13. SameSite=Strict bypass via client-side redirect
If a same-origin endpoint causes a client-side redirect to the sensitive action, and the attacker sends the victim to the redirector, the final navigation is treated as same-site by the browser; the Strict cookie travels.
14. SameSite=Strict bypass via sibling subdomain
`SameSite=Strict` cookies still travel on requests within the same registrable domain (`*.target.com`). A subdomain takeover or an XSS on any subdomain becomes a launchpad for CSRF against the parent.
15. JSON CSRF via `text/plain` content-type
`text/plain` is a CORS "simple" type, no preflight; if the server parses the body as JSON regardless of content type, the CSRF succeeds.
16. Multipart/form-data CSRF
`multipart/form-data` is also a simple content type; if the backend parses it loosely, this bypasses content-type-based filters.
17. HTTP method override (`_method`, `X-HTTP-Method-Override`)
Some frameworks honor `_method=PUT` or `_method=DELETE` in form bodies, or `X-HTTP-Method-Override` headers, to convert a POST into PUT/DELETE. If the framework applies CSRF only to certain methods, this bypasses the protection:
18. CSRF on the login endpoint (login CSRF)
Forces the victim to log in to the attacker's account. The victim then types their next actions (search, checkout, configuration) into an account the attacker can read.
19. CSRF via subdomain takeover
If session cookies are scoped `Domain=.target.com`, ANY subdomain can read/send them. A claimed-but-unused subdomain (DNS still points there, hosting unclaimed) allows the attacker to host JavaScript on `legacy.target.com` and perform same-site CSRF on `app.target.com` while bypassing `SameSite=Strict`.
20. Clickjacking + CSRF token-bearing form
When the form has a CSRF token but the page lacks `frame-ancestors`, the attacker frames the form and clickjacks the user into pressing Submit. The browser includes the token because the framed page generated it. This bypasses CSRF tokens through UI redress; defense is `frame-ancestors`, not token rotation.
Common mistakes
- ●Reporting "no CSRF token" without demonstrating impact -- always show the resulting state change (account takeover, money loss, deletion).
- ●Forgetting to test absent vs empty vs malformed token.
- ●Missing GET-based variants on legacy endpoints.
- ●Reporting logout CSRF as critical -- triagers downgrade these.
- ●Not chaining email-change CSRF with password reset.
- ●Forgetting to test method override.
- ●Forgetting to test in multiple browsers; `SameSite` behavior differs.
SECTION 12. Proof of Concept
Burp Suite step by step
- ●1. Capture the state-changing request in Burp Proxy.
- ●2. Right-click -> "Engagement tools" -> "Generate CSRF PoC".
- ●3. Burp produces auto-submitting HTML; copy to a file and host it.
- ●4. Open the file in a browser session where a test user is logged in to the target.
- ●5. Action fires; verify state change in the target.
Python PoC generator
Classic POST PoC (email change)
Password change PoC (no old password required)
Money transfer PoC
Zero-click admin GET PoC
Or single redirect form:
JSON CSRF (text/plain bypass) PoC
Multipart/form-data CSRF PoC
SameSite=Lax bypass via top-level GET PoC
Method override CSRF PoC
Referer-strip PoC
Token-theft via XSS PoC
Use only inside an XSS context where same-origin reads are possible.
Login CSRF PoC
Bash one-liner detection
If the response is 200 OK and the email actually updated, the endpoint accepts requests without CSRF defense.
SECTION 13. Payloads
Organized by tier. Start with the lightest and only escalate if needed.
Tier 1: classic POST auto-submit
Tier 2: GET-based
Tier 3: JSON via text/plain
Tier 4: multipart/form-data
Tier 5: Referer-strip
Tier 6: Referer regex bypasses (URL shapes)
Tier 7: empty / missing token
Tier 8: token from another session
Tier 9: SameSite=Lax bypass
Tier 10: SameSite Lax + POST 2-minute window (Chrome)
Tier 11: method override
Tier 12: login CSRF
Tier 13: token theft via XSS (requires XSS)
SECTION 14. Wordlists and Payload Libraries
- ●PayloadsAllTheThings -- CSRF Injection: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CSRF%20Injection
- ●HackTricks -- CSRF (Cross-Site Request Forgery): https://book.hacktricks.xyz/pentesting-web/csrf-cross-site-request-forgery
- ●PortSwigger Web Security Academy -- CSRF: https://portswigger.net/web-security/csrf
- ●PortSwigger -- Bypassing CSRF token validation: https://portswigger.net/web-security/csrf/bypassing-token-validation
- ●PortSwigger -- Bypassing SameSite cookie restrictions: https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions
- ●PortSwigger -- Bypassing Referer-based CSRF defenses: https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses
- ●OWASP CSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- ●Intigriti -- A complete guide to exploiting advanced CSRF vulnerabilities: https://www.intigriti.com/researchers/blog/hacking-tools/csrf-a-complete-guide-to-exploiting-advanced-csrf-vulnerabilities
- ●YesWeHack -- Ultimate guide to CSRF vulnerabilities: https://www.yeswehack.com/learn-bug-bounty/ultimate-guide-csrf-vulnerabilities
- ●Tuhin1729 Bug Bounty Methodology -- CSRF: https://github.com/tuhin1729/Bug-Bounty-Methodology/blob/main/CSRF.md
- ●reddelexc Top CSRF Reports corpus: https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md
- ●MDN -- SameSite cookies: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
- ●MDN -- Practical CSRF prevention guide: https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSRF_prevention
- ●Burp Suite -- CSRF PoC generator (built into Pro)
- ●OWASP ZAP -- CSRF scanner (built in)
- ●CyberSamir -- CSRF attacks bypassing SameSite cookies: https://blog.cybersamir.com/csrf-attacks-bypassing-samesite-cookies/
- ●Anas Magane Pentesting Notes (CSRF): https://github.com/Anas-Magane/Pentesting/tree/main/TOP_10_OWASP
Practical advice
- ●Keep a 6-line template for every CSRF shape ready to paste: classic POST, GET image, JSON text/plain, multipart, top-level GET, method override.
- ●Maintain a personal list of 9 Referer-bypass URL shapes for quick testing.
- ●Save a few realistic decoy HTML pages (newsletter, free trial, prize) to wrap PoCs in for delivery-readiness.
- ●Build a Burp macro that auto-rotates token-bypass variants across a request.
SECTION 15. Impact
The impact ladder for CSRF goes from no-effect to full account compromise depending on which endpoint is reachable.
Step 1: nuisance state changes
Toggling a notification preference, marking something as read, dismissing a banner. Low impact; usually rated informational.
Step 2: profile modification
Changing display name, bio, avatar, phone. Mid-low impact; can be useful for social engineering against the victim's network.
Step 3: email change
The single most valuable CSRF outcome. Email change enables password reset to attacker, which enables full account takeover. Email-change CSRFs typically pay 5,000+ USD on mature programs.
Step 4: password change without old-password requirement
Direct ATO from one form submission. Catastrophic; routinely critical-rated.
Step 5: 2FA disable
Removes the second factor; combined with a known/breached password = ATO.
Step 6: financial actions
Money transfer, cryptocurrency send, refund request, payment method change. Direct monetary loss.
Step 7: admin or moderator actions
Bulk delete, ban users, promote/demote, change configurations. Cascading impact across the user base.
Step 8: integration tampering
Change webhook URL to attacker, register attacker as OAuth client, modify API destinations. Long-term data exfiltration.
Step 9: privilege grants
Add attacker as collaborator on a repository, share a private resource, accept an invite. Persistent access without re-auth.
Step 10: account takeover chains
Email change -> password reset -> login. The most common CSRF -> ATO flow.
Step 11: mass / wormable impact
If the PoC can be embedded in a high-traffic page (forum post, social media, supply-chain CDN), many victims can be affected with one delivery. Historical CSRF worms include Netflix (2008) and YouTube (2008).
Step 12: cross-property compromise
With shared SSO or shared cookie domains, CSRF on one property can affect others (organization-wide GSuite, Microsoft 365 tenants).
Step 13: data destruction
Permanent deletion of user data or admin-side records. Recovery may be impossible.
Step 14: regulatory and contractual fallout
GDPR (consent and data integrity), HIPAA, PCI DSS violations.
Step 15: long-tail cost
Forensic investigation, remediation cost, audit fees, customer trust erosion, insurance premium adjustments.
SECTION 16. Prevention
The fix is layered. Each layer is small; together they form defense in depth.
The two rules that cover most cases
- ●1. Every state-changing endpoint validates a CSRF token bound to the user's session.
- ●2. Session cookies are `SameSite=Strict` (or `Lax` with care) and `Secure`.
If both hold, classical CSRF is blocked. The other layers protect against bypasses and chains.
Defense layer 1: synchronizer CSRF token
The classical pattern. Server generates a random per-session token, includes it in every form, validates on submission.
Server-side comparison must be constant-time and bound to the session. Tokens must be random (`secrets.token_urlsafe(32)` or equivalent) and rotated on login/privilege change.
Defense layer 2: SameSite cookie attribute
- ●`SameSite=Strict` -- cookie not sent on any cross-site request. Highest protection. May break legitimate cross-site flows (third-party SSO embeds, OAuth callbacks if not configured to use a non-strict cookie for those flows).
- ●`SameSite=Lax` -- cookie sent on top-level GET navigations only. Modern default; blocks classic POST CSRF.
- ●`SameSite=None` -- cookie sent on all cross-site requests; MUST also be `Secure`. Vulnerable to CSRF without other defenses.
For maximum protection on session cookies, use `Strict`. Use `Lax` if you need cookies on cross-site navigations (e.g., a user clicking a link to your site from email).
Defense layer 3: Origin / Referer validation
Use exact matching, not substring. Reject when both `Origin` and `Referer` are absent on writes (default-deny rather than default-allow).
Defense layer 4: custom required header
A cross-origin page cannot set arbitrary headers without a CORS preflight. A correctly configured server refuses the preflight from untrusted origins.
Defense layer 5: content-type restriction
This blocks `application/x-www-form-urlencoded`, `multipart/form-data`, and `text/plain` -- the three CORS "simple" content types that fire without preflight. But the server must also actually parse only `application/json`, not silently accept JSON in other types.
Defense layer 6: re-authentication for high-risk actions
Even if every other defense fails, the attacker does not know the victim's password and cannot fill the field.
Defense layer 7: no state changes via GET
Map deletes/updates/transfers to POST/PUT/DELETE only. Refuse GET on these endpoints. This eliminates `<img>`/`<iframe>` CSRF entirely on those routes.
Defense layer 8: framework defaults
- ●Django ==> `CsrfViewMiddleware` is on by default in modern versions; never use `@csrf_exempt` on sensitive views.
- ●Spring Security ==> CSRF is on by default; never call `http.csrf().disable()` unless the application is fully stateless and only uses bearer tokens.
- ●Rails ==> `protect_from_forgery with: :exception` is on by default; never call `skip_before_action :verify_authenticity_token`.
- ●ASP.NET MVC ==> use `[ValidateAntiForgeryToken]` on POST actions; in Razor pages, the antiforgery token is automatic.
- ●Express ==> add `csurf` (or modern replacement) middleware and use it consistently on state-changing routes.
- ●Flask ==> use `Flask-WTF` with `CSRFProtect`.
Cookie hardening checklist
Avoid `Domain=.parent.com` unless absolutely required; it leaks cookies to every subdomain, including potentially compromised ones.
Developer checklist
- ●Every state-changing endpoint has a CSRF token validated server-side.
- ●Tokens are random, per-session, rotated on login/privilege change.
- ●Session cookies are `SameSite=Strict` (or Lax with care), `Secure`, `HttpOnly`.
- ●Origin/Referer validation is server-side and uses exact match, not substring.
- ●Custom `X-Requested-With` header is required on AJAX writes and validated server-side.
- ●Sensitive endpoints accept only `application/json` and reject `text/plain`/`form-urlencoded`/`multipart` on writes.
- ●No `@csrf_exempt`, no `http.csrf().disable()`, no `skip_before_action :verify_authenticity_token`, no missing `[ValidateAntiForgeryToken]`.
- ●High-risk endpoints (transfer, password change, 2FA disable) require password re-entry.
- ●No state-changing GET routes.
- ●Token validation is constant-time and fails closed.
- ●CSRF defenses are tested in CI on every release.
- ●Subdomain cookies are scoped narrowly; subdomain takeover risk is monitored.
Enterprise mitigations
- ●CDN / WAF rules that block writes missing CSRF tokens.
- ●Service mesh policies requiring custom headers on internal microservices.
- ●Centralized token issuance and validation service.
- ●SAST rules that flag `@csrf_exempt`, `csrf().disable()`, missing `[ValidateAntiForgeryToken]`, missing `verify_authenticity_token`.
- ●DAST coverage that includes CSRF on every release.
- ●CSP `Sec-Fetch-Site` header validation as an additional check (browsers send this on every request indicating where it came from; reject `cross-site` writes).
- ●Bug bounty programs explicitly in-scope for CSRF.
Sec-Fetch-Site as a modern auxiliary defense
Modern browsers automatically send `Sec-Fetch-Site` on every request. Reject writes when this header is `cross-site`:
This complements the other layers without requiring user-agent JavaScript.
SECTION 17. Real-World Cases
Historical landmarks
- ●Netflix (2008) -- A CSRF on Netflix account settings let attackers modify the user's DVD queue, shipping address, and rental history. Triggered an industry-wide adoption of CSRF tokens.
- ●YouTube (2008) -- Multiple state-changing endpoints (add to playlist, subscribe, comment) lacked CSRF protection. Documented by researchers and led to Google-wide CSRF auditing.
- ●ING Direct (2008) -- Documented academic research showed CSRF could initiate transfers; banks accelerated SCA and CSRF token deployment.
- ●Twitter (2010-2017) -- Multiple CSRF disclosures including direct-message CSRF chains; Twitter rolled out custom-header validation and `SameSite` policies in stages.
Recent disclosed HackerOne bug bounty reports
- ●Dropbox -- Exfiltrate Google Drive access token using CSRF, paid $1,728.
Listing: https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md Lesson: CSRF on integration consent endpoints leaks OAuth tokens for third-party services.
- ●Internet Bug Bounty -- Argo CD CSRF leads to Kubernetes cluster compromise, paid $4,660.
Lesson: CSRF on infrastructure dashboards is critical because the attacker reaches the cluster control plane.
- ●Internet Bug Bounty -- Apache Airflow: missing CSRF protection on DAG/trigger (CVE-2023-49920), paid $0 (IBB).
NVD: https://nvd.nist.gov/vuln/detail/CVE-2023-49920 Lesson: workflow orchestration tools that lack CSRF tokens let attackers trigger arbitrary jobs.
- ●TikTok -- CSRF on TikTok Ads Portal, paid $1,000.
Lesson: ad-management endpoints are sensitive; budget changes and campaign modifications via CSRF translate to direct financial loss.
- ●HackerOne -- HackerOne reports escalation to JIRA is CSRF vulnerable, paid $500.
Lesson: even security-focused platforms ship CSRF; integration paths are common gaps.
- ●Slack -- CSRF in GitHub integration, paid $500.
Listing: https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md Lesson: integration management is a CSRF target with persistent access impact.
- ●Shopify -- H1514 CSRF in domain transfer, listing on top corpus.
Lesson: CSRF in account-level resource transfers can grant attacker control over victim's domains.
- ●Shopify -- [h1-2102] Wholesale -- CSRF to generate invitation token for a customer.
Lesson: wholesale and partner programs often run on older code with weaker defenses.
- ●Mail.ru -- Disable 2FA via CSRF (leads to 2FA bypass).
Lesson: 2FA disable endpoints are particularly valuable CSRF targets.
- ●Mozilla -- CSRF to information disclosure on password reset, listing on corpus.
Lesson: password reset flows often combine multiple endpoints; CSRF on any one of them can leak account info.
- ●IBM -- POST-based CSRF on endpoint leading to modification of contact information, listing on corpus.
- ●X (formerly Twitter) -- CSRF on https://www.niche.co leads to "account disconnection", paid $0.
- ●VK.com -- CSRF for setting email on account, listing on corpus.
- ●Ubiquiti -- Firmware download/install vulnerable to CSRF, listing on corpus.
Lesson: IoT and network appliance interfaces are increasingly CSRF-attacked because impact reaches device firmware.
- ●Elastic -- CSRF in AppSearch allows creation of "curations", listing on corpus.
- ●GSA Bounty -- CSRF on the Federalist API (all endpoints), using Flash file on the attacker's host, listing on corpus.
Note: Flash is dead, but the pattern (cross-origin POST with custom content-type) survives via other primitives.
- ●WakaTime -- JSON CSRF on POST Heartbeats API, listing on corpus.
Lesson: JSON APIs without `application/json` enforcement remain CSRF-vulnerable via `text/plain`.
- ●CS Money -- Site-wide CSRF on Safari due to CORS misconfiguration, paid $300.
Lesson: browser-specific CSRF surfaces persist; differential testing across browsers pays off.
- ●Coinbase -- CSRF on "Set as primary" option on the accounts page, paid $100.
- ●Krisp -- Authentication CSRF resulting in unauthorized account access on Krisp app, listing on corpus.
- ●U.S. Dept of Defense -- CSRF Attack leads to delete album, listing on corpus.
- ●HackerOne (self) -- Timing attack towards endpoints on the web without CSRF, listing on corpus.
- ●Mavenlink -- Clickjacking & CSRF attack can be done at https://app.mavenlink.com/login, listing on corpus.
Lesson: chaining clickjacking with CSRF makes the form-token defense useless.
Curated corpora
- ●reddelexc Top CSRF reports: https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md
- ●HackerOne Hacktivity (CSRF filter): https://hackerone.com/hacktivity?queryString=CSRF
- ●NVD recent CSRF CVEs: https://nvd.nist.gov/vuln/search/results?query=CSRF
Lessons learned
- ●CSRF is alive in 2026 on every category of site, from infrastructure (Argo CD, Apache Airflow) to consumer products (TikTok, Coinbase, Dropbox).
- ●Bug bounty payouts range from $100 (minor settings change) to $4,660+ (cluster compromise) depending on chained impact.
- ●The fix is universally the same shape: token + SameSite + Origin/Referer + custom header + content-type lock + re-auth on high-risk.
- ●Modern frameworks ship CSRF protection by default; the bug usually appears when a developer explicitly disabled it or chose a non-standard endpoint pattern.
- ●Chains pay more: CSRF + email change -> ATO; CSRF + subdomain takeover -> cross-property; CSRF + clickjacking -> token-bearing form submission; CSRF + XSS -> token theft + forged write.
SECTION 18. References
Standards and authoritative docs
- ●OWASP CSRF: https://owasp.org/www-community/attacks/csrf
- ●OWASP CSRF Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
- ●CWE-352 Cross-Site Request Forgery: https://cwe.mitre.org/data/definitions/352.html
- ●OWASP Top 10 (2021) -- CSRF mapped under A01 Broken Access Control: https://owasp.org/Top10/
- ●RFC 6265 (HTTP State Management Mechanism / cookies): https://www.rfc-editor.org/rfc/rfc6265
- ●RFC 6265bis (SameSite cookies, current draft): https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis
Browser documentation
- ●MDN -- SameSite cookies: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value
- ●MDN -- CSRF prevention practical guide: https://developer.mozilla.org/en-US/docs/Web/Security/Practical_implementation_guides/CSRF_prevention
- ●MDN -- Sec-Fetch-Site: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Site
- ●MDN -- Cross-Origin Resource Sharing (CORS): https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Learning resources
- ●PortSwigger Web Security Academy -- CSRF: https://portswigger.net/web-security/csrf
- ●PortSwigger -- Bypassing CSRF token validation: https://portswigger.net/web-security/csrf/bypassing-token-validation
- ●PortSwigger -- Bypassing SameSite cookie restrictions: https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions
- ●PortSwigger -- Bypassing Referer-based defenses: https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses
- ●HackTricks -- CSRF: https://book.hacktricks.xyz/pentesting-web/csrf-cross-site-request-forgery
- ●PayloadsAllTheThings -- CSRF Injection: https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/CSRF%20Injection
- ●Intigriti -- CSRF guide: https://www.intigriti.com/researchers/blog/hacking-tools/csrf-a-complete-guide-to-exploiting-advanced-csrf-vulnerabilities
- ●YesWeHack -- Ultimate CSRF guide: https://www.yeswehack.com/learn-bug-bounty/ultimate-guide-csrf-vulnerabilities
- ●Tuhin1729 -- CSRF methodology: https://github.com/tuhin1729/Bug-Bounty-Methodology/blob/main/CSRF.md
- ●CyberSamir -- Bypassing SameSite: https://blog.cybersamir.com/csrf-attacks-bypassing-samesite-cookies/
Framework documentation
- ●Django CSRF middleware: https://docs.djangoproject.com/en/stable/ref/csrf/
- ●Spring Security CSRF: https://docs.spring.io/spring-security/reference/servlet/exploits/csrf.html
- ●Flask-WTF CSRFProtect: https://flask-wtf.readthedocs.io/en/latest/csrf/
- ●Ruby on Rails security guide -- CSRF: https://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf
- ●Express csurf (legacy reference): https://github.com/expressjs/csurf
- ●ASP.NET Core AntiForgery: https://learn.microsoft.com/en-us/aspnet/core/security/anti-request-forgery
Tools
- ●Burp Suite (CSRF PoC generator, repeater): https://portswigger.net/burp
- ●OWASP ZAP (built-in CSRF scanner): https://www.zaproxy.org/
- ●Nuclei (templates for missing-token and other detections): https://github.com/projectdiscovery/nuclei
- ●reddelexc CSRF report corpus: https://github.com/reddelexc/hackerone-reports/blob/master/tops_by_bug_type/TOPCSRF.md
CVE/advisory feeds
- ●NVD: https://nvd.nist.gov
- ●GitHub Advisories: https://github.com/advisories
- ●Apache security: https://security.apache.org
- ●Anas Magane Pentesting Notes: https://github.com/Anas-Magane/Pentesting
SECTION 19. Practical Labs
Planned ANAS CSRF Labs (SOON)
- ●ANAS-CSRF-01 -- AnasBank Classic POST Money Transfer, beginner
- ●ANAS-CSRF-02 -- AnasMarket Email Change leading to ATO, beginner
- ●ANAS-CSRF-03 -- AnasDocs Password Change Without Old Password, beginner
- ●ANAS-CSRF-04 -- AnasCorp Admin GET-based Zero-Click Deletion, beginner-intermediate
- ●ANAS-CSRF-05 -- AnasOne JSON CSRF via text/plain Bypass, intermediate
- ●ANAS-CSRF-06 -- AnasMarket Multipart/Form-Data CSRF, intermediate
- ●ANAS-CSRF-07 -- AnasOne Empty Token Bypass, intermediate
- ●ANAS-CSRF-08 -- AnasMarket Token From Different Session Bypass, intermediate
- ●ANAS-CSRF-09 -- AnasBank SameSite=Lax Top-Level GET Bypass, advanced
- ●ANAS-CSRF-10 -- AnasBank Lax + POST 2-Minute Window (Chrome), advanced
- ●ANAS-CSRF-11 -- AnasSocial Referer Regex Bypass (9 URL shapes), advanced
- ●ANAS-CSRF-12 -- AnasCorp Method Override (_method, X-HTTP-Method-Override), advanced
- ●ANAS-CSRF-13 -- AnasOne CSRF + XSS Token Theft Chain, advanced
- ●ANAS-CSRF-14 -- AnasMarket Login CSRF for Credit Card Harvesting, advanced
- ●ANAS-CSRF-15 -- AnasMarket Subdomain Takeover + Cookie-Domain CSRF, expert
- ●ANAS-CSRF-16 -- AnasCorp Compound Chain (CSRF + Clickjacking + Email Change -> ATO), expert
PortSwigger Web Security Academy CSRF labs
- ●CSRF vulnerability with no defenses (APPRENTICE): https://portswigger.net/web-security/csrf/lab-no-defenses
- ●CSRF where token validation depends on request method (PRACTITIONER): https://portswigger.net/web-security/csrf/lab-token-validation-depends-on-request-method
- ●CSRF where token validation depends on token being present (PRACTITIONER): https://portswigger.net/web-security/csrf/lab-token-validation-depends-on-token-being-present
- ●CSRF where token is not tied to user session (PRACTITIONER): https://portswigger.net/web-security/csrf/lab-token-not-tied-to-user-session
- ●CSRF where token is tied to non-session cookie (PRACTITIONER): https://portswigger.net/web-security/csrf/lab-token-tied-to-non-session-cookie
- ●CSRF where token is duplicated in cookie (PRACTITIONER): https://portswigger.net/web-security/csrf/lab-token-duplicated-in-cookie
- ●SameSite Lax bypass via method override (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-lax-bypass-via-method-override
- ●SameSite Strict bypass via client-side redirect (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-strict-bypass-via-client-side-redirect
- ●SameSite Strict bypass via sibling domain (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-strict-bypass-via-sibling-domain
- ●SameSite Lax bypass via cookie refresh (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-samesite-restrictions/lab-samesite-lax-bypass-via-cookie-refresh
- ●CSRF where Referer validation depends on header being present (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses/lab-referer-validation-depends-on-header-being-present
- ●CSRF with broken Referer validation (PRACTITIONER): https://portswigger.net/web-security/csrf/bypassing-referer-based-defenses/lab-referer-validation-broken
Self-hosted lab targets
- ●bWAPP -- CSRF modules: http://www.itsecgames.com
- ●DVWA -- CSRF lessons: https://github.com/digininja/DVWA
- ●WebGoat (OWASP) -- CSRF lesson: https://github.com/WebGoat/WebGoat
- ●Juice Shop (OWASP) -- multiple CSRF challenges: https://github.com/juice-shop/juice-shop
Lab progression suggestion
- ●Week 1: PortSwigger Apprentice lab + ANAS-CSRF-01/02/03 + sections 1-8 of this course.
- ●Week 2: PortSwigger Practitioner token-validation labs + ANAS-CSRF-04 to 08 + read 5 disclosed bounty reports in section 17.
- ●Week 3: PortSwigger SameSite bypass labs + ANAS-CSRF-09/10/11 + replicate one CVE in a sandbox.
- ●Week 4: PortSwigger Referer bypass labs + ANAS-CSRF-12 to 14.
- ●Week 5: ANAS-CSRF-15/16 (chains) + start hunting on programs that scope CSRF explicitly.
SECTION 20. Cheat Sheet
SECTION 21. Exam
Thirty multiple-choice questions. Answer key at the end.
- ●1. The CWE for CSRF is:
A) CWE-79 B) CWE-89 C) CWE-352 D) CWE-1021
- ●2. Why does CSRF work?
A) Browsers send cookies with every request to the cookie's domain regardless of where the request originated B) Cookies are encrypted by the attacker C) HTTPS is not enforced D) WAFs are disabled
- ●3. The two conditions for any CSRF attack are:
A) Open ports + weak passwords B) Victim authenticated to target + attacker can cause the browser to send a request to target C) Internal IP + DNS D) Domain takeover + open S3
- ●4. CORS prevents CSRF?
A) Yes, always B) No, CORS limits reading responses, not sending requests C) Only on HTTPS D) Only on Firefox
- ●5. `SameSite=Strict` cookies are:
A) Sent on all cross-site requests B) Never sent on cross-site requests C) Sent only on POST D) Sent only on GET
- ●6. `SameSite=Lax` cookies are sent on:
A) All cross-site requests B) Top-level GET navigations (and not on cross-site POST or background GETs) C) Cross-site POST only D) Never
- ●7. The most reliable JSON CSRF bypass uses:
A) `application/json` Content-Type (browsers preflight) B) `text/plain` Content-Type which is a CORS simple type and does not trigger preflight C) `application/xml` D) None of the above
- ●8. The classic POST CSRF PoC is:
A) A `<script>` tag with `eval` B) An auto-submitting `<form>` with `<body onload>` or `<script>document.forms[0].submit()</script>` C) An SVG file D) A cookie
- ●9. The payload `old=KNOWN&new=AttackerControlled&confirm=AttackerControlled` submitted via CSRF changes:
A) The user's email B) The user's password (when the server expects old/new/confirm) C) The user's avatar D) None
- ●10. A password change endpoint that does NOT require the old password is:
A) Safer B) Catastrophic when CSRF-vulnerable: one form submit = ATO C) Required by GDPR D) Default in Django
- ●11. The single most effective auxiliary defense against classic POST CSRF in 2026 is:
A) HTTPS B) `SameSite=Strict` (or Lax) on session cookies C) CAPTCHA D) Rate limiting
- ●12. A CSRF token stored only in a cookie with no header/body comparison is:
A) Strong protection B) Ineffective because cookies attach automatically (the attacker does not need to know it) C) Required for compliance D) Modern best practice
- ●13. A server that validates `Referer` only when present can be bypassed by:
A) Setting `<meta name="referrer" content="no-referrer">` to strip the header B) HTTPS upgrade C) Adding a custom header D) Disabling JavaScript
- ●14. Login CSRF allows the attacker to:
A) Steal the victim's password B) Force the victim to be logged in to an attacker-controlled account so the victim's subsequent actions land in attacker's account C) Crash the browser D) Encrypt the session
- ●15. The Apache Airflow missing CSRF protection on DAG trigger CVE is:
A) CVE-2021-44228 B) CVE-2023-49920 C) CVE-2017-5638 D) CVE-2025-66516
- ●16. Argo CD's CSRF bounty (Internet Bug Bounty) paid approximately:
A) $50 B) $500 C) $4,660 D) $50,000
- ●17. Dropbox's "Exfiltrate Google Drive access token using CSRF" bounty paid:
A) $1,728 B) $172,800 C) $17 D) $0
- ●18. Which Referer regex bypass passes a substring check `referer.includes('target.com')`?
A) https://target.com/legitimate B) https://attacker.com#target.com C) https://attacker.com (no tricks) D) https://other.com
- ●19. A SameSite=Lax cookie may be sent on cross-site POST in Chrome:
A) Never B) Within a short window (historically ~2 minutes) after the cookie was created C) Always for HTTPS D) Only when JavaScript is disabled
- ●20. Method override CSRF uses:
A) `_method=PUT` or `X-HTTP-Method-Override` to convert POST into PUT/DELETE if the framework honors it B) JSON injection C) HTTP/2 stream multiplexing D) WebSocket framing
- ●21. The `application/x-www-form-urlencoded` Content-Type:
A) Triggers CORS preflight B) Is a CORS simple type that does NOT trigger preflight (which is why classic POST CSRF works) C) Cannot be used with cookies D) Cannot carry JSON
- ●22. Disabling Spring Security CSRF with `http.csrf().disable()`:
A) Strengthens defense B) Removes the framework's default CSRF protection -- a frequent regression C) Is required by Spring 6 D) Affects only HTTPS
- ●23. A `@csrf_exempt` decorator in Django:
A) Strengthens CSRF B) Removes Django's default CSRF middleware for that view C) Enables CORS D) Sets SameSite
- ●24. Which combination demonstrates account takeover via CSRF?
A) CSRF on logout B) CSRF on email change + password reset to attacker's email + attacker login C) CSRF on profile picture D) CSRF on theme toggle
- ●25. CSRF + XSS chain works because:
A) XSS lets the attacker read same-origin pages and steal the CSRF token, then submit forged writes with the stolen token B) Cookies are doubled C) HTTPS is bypassed D) WAFs are disabled
- ●26. CSRF + clickjacking chain works because:
A) The user is clicked into submitting a real (token-bearing) form inside an iframe, so the token is valid B) JavaScript is disabled C) Cookies are stolen D) The form is replaced
- ●27. Subdomain takeover + CSRF chain works because:
A) Cookies scoped to `Domain=.target.com` are sent to all subdomains, including the attacker-claimed one B) DNS is encrypted C) HTTPS is upgraded D) WAF is disabled
- ●28. Sec-Fetch-Site is:
A) A browser-sent header indicating where the request came from (same-origin, same-site, cross-site, none) B) A CSRF token C) A cookie attribute D) A WAF rule
- ●29. The most overlooked CSRF target on most engagements is:
A) The integration/webhook configuration endpoint B) The 404 page C) The robots.txt D) The favicon
- ●30. The MOST important takeaway about CSRF:
A) Browsers handle it automatically B) The victim does nothing wrong; the browser is tricked into firing an authenticated request, and only multi-layer defenses (token + SameSite + Origin + content-type + re-auth) cover modern bypasses C) CSRF is extinct in 2026 D) CORS prevents all CSRF
Answer key
- ●1.C 2.A 3.B 4.B 5.B 6.B 7.B 8.B 9.B 10.B
- ●11.B 12.B 13.A 14.B 15.B 16.C 17.A 18.B 19.B 20.A
- ●21.B 22.B 23.B 24.B 25.A 26.A 27.A 28.A 29.A 30.B
Scoring
- ●27 to 30: CSRF expert.
- ●24 to 26: Solid.
- ●19 to 23: Functional; re-read sections 11 and 16.
- ●Below 19: re-read sections 1 to 8 and retake.
SECTION 22. Certificate Requirements
- ●Read all 24 sections of this course.
- ●Score 24/30 or higher on section 21.
- ●Complete all PortSwigger Web Security Academy CSRF labs listed in section 19 (12 labs).
- ●Complete at least 10 of the 16 planned ANAS CSRF Labs (once released).
- ●Demonstrate one end-to-end CSRF -> ATO chain against a controlled target you own.
- ●Document one CSRF finding in a write-up of 500+ words, with HTTP traces, screenshots, the bypass that worked, and the exact fix.
- ●Maintain a personal payload library of 20+ CSRF templates organized by tier.
Ethical baseline
The techniques here work against real authenticated sessions. Use them only on systems you own or have explicit written permission to test. Hosting a CSRF PoC and tricking real users into visiting it is a criminal act in every jurisdiction this course is taught in.
SECTION 23. Important Notes
Common beginner mistakes
- ●Confusing CSRF with CORS. CSRF forges requests; CORS limits reading responses. Different layers.
- ●Confusing CSRF with Clickjacking. CSRF needs no click; Clickjacking needs a click.
- ●Confusing CSRF with XSS. XSS injects code into the target's page; CSRF makes the browser send a request from elsewhere.
- ●Reporting "no CSRF token" without demonstrating impact. Always escalate to ATO, transfer, deletion, or admin action.
- ●Forgetting to test empty/absent/foreign tokens, not only "no token".
- ●Missing GET-based variants on legacy admin endpoints.
- ●Reporting CSRF on logout or trivial settings as critical.
- ●Skipping SameSite bypasses on modern targets.
Pentester tips
- ●Use Burp's right-click "Generate CSRF PoC" as a starting template, then customize per bypass.
- ●Map session cookie attributes for every authenticated cookie (SameSite, Secure, HttpOnly, Domain). Subdomain takeover risk lives in `Domain`.
- ●Walk every state-changing endpoint with the same checklist of variations.
- ●Test in Chrome, Firefox, and Safari -- SameSite behavior differs subtly between engines.
- ●Save full HTTP traces and screenshots for every confirmed CSRF; reports without them get downgraded.
Bug bounty tips
- ●CSRF on email change or password change endpoints regularly pays in the $5,000-$20,000 range on mature programs.
- ●CSRF on transfer/payment endpoints or admin destructive actions can pay $10,000-$50,000.
- ●Reports must demonstrate concrete impact (state change, ATO, fund loss) -- "no CSRF token" alone gets rejected as informational.
- ●Combine with XSS, CORS misconfiguration, subdomain takeover, or clickjacking for compound severity.
- ●Include a hosted PoC URL, an HTTP trace, and a video/screenshot of the action firing in a fresh session.
Red team tips
- ●CSRF is quiet: no malware, no exploits, no credential theft.
- ●Useful in long-running campaigns where you silently modify victim accounts over time (e.g., add backup email, register attacker as recovery contact).
- ●Combined with phishing infrastructure: the phishing page can be the CSRF launch page; victim never has to type credentials.
- ●Login CSRF is underused but devastating against e-commerce, fintech, and ad platforms where victim data accumulates in the attacker's account.
Defender tips
- ●Apply CSRF defenses through centralized middleware, never per-controller. Per-controller decisions are how regressions ship.
- ●Treat any `@csrf_exempt`, `http.csrf().disable()`, missing `[ValidateAntiForgeryToken]`, or `skip_before_action :verify_authenticity_token` as a code-review block.
- ●Enforce SameSite at the cookie issuance point; do not leave it to template defaults.
- ●Add a CI check that asserts the presence and validation of CSRF tokens on every state-changing route.
- ●Use `Sec-Fetch-Site: cross-site` to reject writes; modern browsers send this automatically.
Real-world advice
- ●Modern frameworks (Django, Rails, Spring Security, Laravel, ASP.NET Core) ship CSRF protection by default. The bug almost always appears when a developer disables it or rolls a custom path.
- ●SPAs that use bearer tokens are not CSRF-vulnerable for token-auth endpoints, but their cookie-auth endpoints (login, OAuth callback, web UI) are.
- ●Mobile-only APIs using bearer tokens are CSRF-immune, but a hybrid app that mixes cookies and tokens often has gaps.
- ●WebSocket connections do not honor the same CSRF model; CSWSH (Cross-Site WebSocket Hijacking) is a related class with its own defenses (validate `Origin` on the WS upgrade).
- ●Integration management endpoints (connect Slack, change webhook URL, register OAuth client) are recurring CSRF targets with persistent-access impact.
Things to remember during exams
- ●CWE-352 = CSRF.
- ●OWASP A01:2021 Broken Access Control covers CSRF (was A8:2017).
- ●CORS does NOT prevent CSRF.
- ●Cookies travel by destination, not by origin.
- ●`text/plain` is the canonical modern JSON CSRF bypass.
- ●SameSite=Strict is the strongest cookie-level defense; SameSite=Lax is the modern browser default.
- ●Defense in depth: token + SameSite + Origin + custom header + content-type + re-auth.
Frequently confused concepts
- ●CSRF vs CORS -- CSRF forges requests; CORS limits reading responses. Different layers.
- ●CSRF vs XSS -- XSS injects code into the target; CSRF causes the browser to send a forged request.
- ●CSRF vs Clickjacking -- Clickjacking requires a click; CSRF does not.
- ●Synchronizer token vs double-submit cookie -- both are tokens, but one is stored in session, the other in a non-session cookie. Both must be compared server-side and bound to the user.
- ●Reflected vs stored CSRF -- reflected fires via attacker page; stored (rare) fires via injection into target's own page.
Interview tips
- ●Be ready to explain why "the user is logged in" is authentication, not authorization-of-intent.
- ●Cite Netflix 2008 as the historical landmark and Argo CD ($4,660 IBB), Dropbox ($1,728), Apache Airflow CVE-2023-49920 as 2023+ examples.
- ●Explain the full defense stack in one breath: token, SameSite, Origin, custom header, content-type, re-auth.
- ●Be able to draw the destination-based cookie attachment diagram on a whiteboard.
- ●Explain why CORS does not prevent CSRF (response readability vs request emission).
Key takeaways
- ●CSRF exists because browsers attach cookies to requests by destination, not by initiator.
- ●The fix is layered; no single layer is enough against modern bypasses.
- ●Bug bounty payouts range from low (settings toggle) to critical (cluster compromise, ATO, financial loss).
- ●Modern frameworks default to safe; the bug appears when defaults are disabled or custom paths skip them.
- ●Real disclosed reports in 2023+ on Argo CD, Apache Airflow, Dropbox, TikTok, Slack, Shopify, Mail.ru, Mozilla, IBM, Coinbase, Ubiquiti -- this is not history.
SECTION 24. Final Word from Your Instructor
You finished the CSRF course. You now know more about this bug class than most working backend developers, and enough to find it, exploit it responsibly, and fix it in any codebase.
Here is the short version.
CSRF works because the browser attaches cookies to requests based on where they are going, not based on where they were initiated. A page on `attacker.com` can ask the browser to send a request to `target.com`, and the browser will attach the cookies it has for `target.com`. The server sees a request that looks like the user just submitted a form on `target.com`. The server cannot tell the difference. The state changes. The bug is born.
The defense is layered: a synchronizer CSRF token bound to the session, `SameSite=Strict` or `Lax` cookies, server-side Origin/Referer validation with exact matching, a required custom header that cannot be forged cross-origin without preflight, content-type lock-down to `application/json` on writes, and re-authentication on high-risk actions. No single layer is enough; together they cover the bypasses listed in section 11. The modern auxiliary check is `Sec-Fetch-Site: cross-site`, which browsers send automatically and which servers can reject on writes.
On the offensive side, the workflow is short. Capture a state-changing request. Strip the token, then the Origin, then the Referer, then swap the content-type, then try method override, then test the SameSite cookie's behavior across browsers. The first variation that succeeds is the report. Escalate to impact: email change -> password reset -> account takeover; transfer -> funds extraction; admin -> bulk deletion.
The disclosed reports prove CSRF is alive in 2026. Argo CD CSRF paid $4,660 for a chain that led to Kubernetes cluster compromise. Dropbox CSRF paid $1,728 for OAuth token exfiltration on a Google Drive integration. Apache Airflow shipped CVE-2023-49920 specifically for missing CSRF protection on DAG trigger endpoints. TikTok paid $1,000 for CSRF on the ads portal. Slack paid $500 for CSRF in the GitHub integration. HackerOne itself disclosed CSRF in its JIRA escalation pipeline. The pattern is not historical; it is current.
When you see a state-changing endpoint, ask: where is the token? What is the cookie's SameSite? Does the server validate Origin? Does it accept text/plain? Does method override work? Is there a sibling subdomain that shares cookies? Is the action reachable via GET? Each question is a probe. Each probe that succeeds is a finding.
Stay curious. Stay ethical. Verify scope before you touch anything. The browser will obey almost anyone who asks correctly; your job is to know when "asking correctly" was actually you, and when it was someone else using your hand.
Go hunt.