Understand how CSRF tricks authenticated users into performing unintended actions, how to craft a proof-of-concept, and the protections that stop it.
CSRF Explained
Cross-Site Request Forgery (CSRF) is an attack that tricks an authenticated user's browser into making an unintended request to a web application. Because the browser automatically attaches cookies to every request, the application sees the request as legitimate — even though the user didn't initiate it.
The impact depends entirely on what the forged request does. Classic examples include changing a user's email address, transferring funds, adding an admin account, or deleting data — all without the victim's knowledge. CSRF is particularly insidious because the victim sees nothing unusual — there's no error, no warning, and often no visible activity at all. The attack happens silently in the background while the user is doing something completely unrelated on a different tab.
Why Browsers Make CSRF Possible — The Cookie Problem
To understand CSRF, you need to understand one fundamental behaviour of web browsers: they automatically attach cookies to every HTTP request sent to a domain, regardless of where that request originated. This was a deliberate design decision made in the early web to support persistent login sessions — without it, you'd have to log in to every page you visited. But this same behaviour is what CSRF exploits.
The valet key analogy: Imagine you hand your car key to a valet at a restaurant. The valet has temporary authority to use your car. Now imagine someone else — a stranger — walks up to the valet stand and says "I need the car from table 12 moved to the exit." If the valet doesn't verify who's asking, they'll comply, because the key proves the authority, not the person making the request. CSRF is exactly this: a third party issues a request using the authority (the session cookie) that belongs to someone else, and the server complies because the credentials check out.
The reason CSRF works — and not every cross-site request is an attack — is the combination of three conditions that must all be true simultaneously:
- The victim is currently authenticated — they have an active session cookie for the target application stored in their browser.
- The request uses only predictable parameters — the attacker can construct a valid request without needing to read anything from the victim's session. If the request requires a random secret token, the attacker can't forge it.
- The browser sends credentials automatically — cookies are sent with cross-origin requests by default (unless the server has configured SameSite restrictions). The browser doesn't discriminate between requests the user explicitly made and requests triggered by code on another page.
Remove any one of these three conditions and the attack fails. CSRF defences — tokens, SameSite cookies, origin header checks — each work by breaking one of these conditions.
Same-Origin Policy — Why It Doesn't Stop CSRF
A common misconception is that the browser's Same-Origin Policy (SOP) prevents CSRF. The SOP prevents JavaScript on one origin from reading responses from another origin — but it does not prevent the browser from sending requests to another origin, and it does not prevent cookies from being attached to those requests. The attack doesn't need to read the response; it just needs the server to process the request. That's the gap CSRF exploits.
Think of it this way: the Same-Origin Policy is a one-way mirror. Malicious JavaScript on evil.com can send a form submission to bank.com with the victim's cookies — it just can't see the response. For state-changing operations like transfers, password changes, or account deletions, that's enough. The attacker doesn't need to see the confirmation page; they just need the action to execute.
The CSRF Attack Flow
Three conditions must be true for a CSRF attack to work: the victim must be authenticated, the application must use predictable request parameters (no random token), and the browser must send credentials automatically with cross-site requests.
1. Victim logs into bank.com → session cookie stored in browser 2. Victim visits attacker-controlled page (evil.com) 3. Attacker page silently submits: POST https://bank.com/transfer amount=5000&to_account=99999 4. Browser automatically attaches victim's bank.com session cookie 5. Bank.com receives a valid, authenticated POST request 6. Bank processes the transfer — it looks legitimate 7. Victim has no idea anything happened
The attacker never needs to be on the same network as the victim. They don't need the victim's password or session token. They don't interact with the victim directly at all. The entire attack is delivered by getting the victim to load a page — which could be a link in an email, a comment on a forum, an ad, or an embedded image. The victim's own authenticated browser does all the work.
CSRF in Practice
The most fundamental CSRF payload is an HTML form that submits itself automatically using JavaScript the moment the page loads. The form targets the vulnerable endpoint, contains all required parameters as hidden fields, and fires before the user sees anything. The page can display completely normal content — a fake news article, an image gallery, anything — while the form submits invisibly in the background.
<!-- Attacker's page at evil.com --> <form action="https://target.com/change-email" method="POST" id="csrf"> <input type="hidden" name="email" value="[email protected]"> <input type="hidden" name="confirm_email" value="[email protected]"> </form> <script>document.getElementById('csrf').submit();</script> <!-- Victim visits page → email silently changed to attacker's address --> <!-- Attacker now triggers "forgot password" → gets reset link → full account takeover -->
The consequence chain here is important: changing an email address alone might seem low-severity. But combined with a password reset flow, it becomes a full account takeover. A single CSRF on an email-change endpoint can be the first step in a two-stage account compromise.
If a state-changing operation is performed via GET request — which is a design error, as GET should be idempotent and read-only — the attack surface is even larger. Browsers automatically make GET requests to load images, and those requests carry cookies. An attacker can embed a zero-pixel image whose URL is actually a destructive endpoint.
Why GET is dangerous for state changes: The HTTP specification defines GET as a "safe" method — one that should never modify server state. Many developers treat this as an optional guideline rather than a security requirement. The browser treats GET requests as inherently safe to make automatically, which is precisely why using GET for state-changing actions creates CSRF vulnerability without any JavaScript at all.
<!-- Can be embedded in an email, a forum post, any HTML content --> <img src="https://target.com/delete-account?confirm=yes" width="0" height="0"> <!-- Browser requests this "image" with victim's session cookie attached --> <!-- Account deleted -- no JavaScript, no user interaction beyond loading the page --> <!-- Another variant: fund transfer --> <img src="https://bank.com/transfer?to=99999&amount=500" width="0" height="0"> <!-- This could appear in any email client that renders HTML -->
The identification process in a web application assessment is systematic: intercept every state-changing request in Burp, check for CSRF tokens, and where absent, verify exploitability by replaying from a different session. A request with no CSRF token is a candidate; a request that succeeds when replayed from a different browser with a different session cookie confirms exploitability.
# Step 1: In Burp, intercept a state-changing POST POST /account/update HTTP/1.1 Host: target.com Cookie: session=abc123 [email protected]&phone=555-1234 # No csrf_token parameter → candidate for CSRF # Step 2: Verify it requires authentication (session check) # Remove Cookie header → should fail → confirms session is required # Step 3: Replay from different session (different browser/incognito) # Use a different session cookie → if the update succeeds → CSRF confirmed # The request doesn't require anything unique to the original user # Step 4: Build PoC HTML page and test against test account
The presence of a CSRF token field in a form does not guarantee protection. Developers frequently implement token validation incorrectly in ways that leave the application still exploitable. Each bypass below represents a real implementation error found in production applications.
# Bypass 1: Remove the token field entirely [email protected] (no csrf_token field at all) # Some servers only check the token if it's present — absence passes # Bypass 2: Submit an empty token [email protected]&csrf_token= # Some servers check non-null but not non-empty # Bypass 3: Use another user's valid token (not session-tied) [email protected]&csrf_token=other_users_known_token # Token valid globally, not per-user → shared token pool vulnerability # Bypass 4: Change POST to GET (method override) GET /account/[email protected] HTTP/1.1 # Some frameworks process GET and POST equally for state changes
CSRF Impact Across Application Types
CSRF severity varies dramatically based on what state-changing operations the application exposes. The following scenarios illustrate why context matters when assessing CSRF findings.
A banking application allows authenticated users to transfer funds via a POST request with recipient account and amount parameters. No CSRF token is present. An attacker crafts a malicious page and distributes it via a phishing email disguised as a bank notification: "Your statement is ready — click here to view." The link leads to a page that displays a fake PDF while silently submitting a fund transfer in the background.
The victim clicks the link while logged into their online banking (which they left open in another tab). The transfer executes immediately. The victim's session cookie — still valid — authenticates the request. The bank's server processes it as a legitimate transfer. No password was phished, no malware was installed. The entire attack was a single HTML page.
A SaaS platform's admin panel allows administrators to create new user accounts via a POST to /admin/users/create. The endpoint lacks a CSRF token. An attacker sends a spear-phishing email to a known admin, containing an embedded image whose loading triggers a GET request that creates a new admin-level backdoor account with attacker-controlled credentials.
The admin opens the email on a device where they're logged into the admin panel. The image loads. The account is created. The attacker now has persistent admin access to the platform without ever having logged in, cracked a password, or triggered any alerts. The compromised admin account has no unusual login activity — the legitimate admin is still using it normally.
This scenario illustrates a critical point: CSRF on privileged endpoints is a persistence mechanism, not just a nuisance. The attacker doesn't need to maintain the CSRF attack after initial compromise — one successful trigger creates a permanent foothold.
A social platform allows users to post content that renders HTML. The platform's profile settings endpoint is vulnerable to CSRF. An attacker posts a message containing a hidden zero-pixel form that auto-submits, targeting the settings endpoint to change the viewer's privacy settings to "public" and add the attacker as a trusted contact.
Every authenticated user who views the attacker's post silently has their settings changed. This is a stored CSRF — the payload persists in the database and fires for every visitor. The impact scales with the popularity of the post. A high-traffic post could affect thousands of users' accounts. Combined with a stored XSS vulnerability, this becomes a self-propagating worm.
This pattern — stored CSRF embedded in user-generated content — was the mechanism behind the 2005 MySpace Samy worm, which spread to over one million profiles in 20 hours using a combination of stored XSS and CSRF to add the attacker as a friend on every profile it touched.
What CSRF Exposure Means for an Organisation
- Session-based authentication is not authentication of intent. A session cookie proves that a browser was used to log in — it does not prove the logged-in user intended to make this specific request. CSRF exploits this gap. Any application with user accounts and state-changing operations has CSRF exposure until tokens or SameSite cookies are in place.
- CSRF on admin endpoints is disproportionately severe. A CSRF on a comment-posting form is low severity. The same vulnerability on an admin user-creation endpoint is critical. A single CSRF finding requires contextual severity assessment against every state-changing endpoint in the application, not just the ones the UI highlights.
- Modern browsers partially mitigate CSRF via SameSite defaults. Chrome and Firefox now default new cookies to SameSite=Lax, which prevents cookies from being sent on cross-site POST requests. However, SameSite=Lax still allows cookies on top-level navigation GET requests, does not protect legacy cookies set before the default changed, and can be bypassed by servers that incorrectly process GET requests as state-changing. CSRF tokens remain the recommended defence alongside SameSite.
- Compliance frameworks require CSRF protection. OWASP ASVS Level 1 (the minimum baseline for any application) requires CSRF tokens or equivalent protection on all state-changing requests. PCI DSS Requirement 6.4 requires web application protection. An application without CSRF protection on payment or account management endpoints fails these requirements.
What You Need to Know
You've covered the theory. Now apply it hands-on in the simulated environment.
Start Lab — CSRF →← Return to all labs