← Blog
Web Security2026-04-1911 min read

CSRF Explained: How Cross-Site Request Forgery Works

CSRF lets an attacker use your own browser against you — triggering authenticated actions on sites you're logged into, without ever touching your credentials. Here's how it works and how to find it.

You're logged into your bank. In another tab, you visit a sketchy forum. The forum loads an invisible image tag that points to your bank's transfer endpoint. Your browser, being helpful, sends your bank cookies along with the request. The transfer goes through.

That's Cross-Site Request Forgery. The attacker didn't steal your credentials. They didn't break your session. They just made your browser do something you didn't intend — using authentication you already had.

How it works

Browsers automatically attach cookies to every request made to the domain that set them. This is what keeps you logged in — your session cookie goes along with every request to the site, including requests initiated by other pages.

CSRF exploits this. An attacker hosts a page that, when loaded, causes your browser to make a request to a target site using your existing session. The target site sees a valid, authenticated request — it has no way to tell that it was triggered by a third-party page, not by you.

<!-- Attacker's page: triggers a state-changing request silently -->
<img src="https://bank.com/transfer?to=attacker&amount=1000" width="0" height="0">

<!-- Or with a form for POST requests -->
<form id="csrf" action="https://bank.com/transfer" method="POST">
  <input type="hidden" name="to" value="attacker">
  <input type="hidden" name="amount" value="1000">
</form>
<script>document.getElementById('csrf').submit();</script>

The victim visits the attacker's page while logged into their bank. Their browser sends the request. The bank processes it. The money moves.

What makes an application vulnerable

Three conditions have to be true simultaneously:

If all three hold, any page on the internet can trigger state-changing actions on behalf of any logged-in user who visits it.

GET vs POST — both can be vulnerable

GET-based state changes are the worst case: a single <img> tag is enough. But POST requests are almost as easy to exploit — a hidden form with auto-submit takes five lines of HTML.

Using POST doesn't protect against CSRF. Only explicit origin verification does.

JSON APIs are often assumed to be CSRF-safe because cross-origin pages can't setContent-Type: application/json without triggering a preflight. But if an endpoint accepts text/plain or application/x-www-form-urlencoded, it's still exploitable — and many endpoints do.

How to find CSRF vulnerabilities

Look for state-changing requests (anything that modifies data — account settings, password, email, payment, permissions) and check whether they include any unpredictable token that an attacker couldn't forge.

# In Burp Suite: capture a state-changing request
POST /account/email HTTP/1.1
Host: target.com
Cookie: session=abc123
Content-Type: application/x-www-form-urlencoded

email=victim@example.com

# No CSRF token in the body or headers?
# Test it: replay this request from a different origin
# If it succeeds, the endpoint is vulnerable

The test is simple: can you replay the request from a different origin without adding any secret the attacker couldn't know? If yes — it's vulnerable.

Things that do NOT prevent CSRF:

Bypassing weak defences

Some applications implement CSRF tokens but do so incorrectly. Common weaknesses:

# Test 1: Remove the CSRF token entirely
POST /account/email
email=attacker@evil.com
# (no csrf_token field)
# → Does it succeed? Token not required.

# Test 2: Use an empty token
POST /account/email
email=attacker@evil.com&csrf_token=
# → Does it succeed? Token validation broken.

# Test 3: Use your own valid token for another user's request
# Get a token from your own session, use it in a cross-account attack
# → Does it succeed? Token not tied to session.

The real defence: CSRF tokens

A CSRF token is a random, unpredictable value generated server-side and tied to the user's session. It's included in every state-changing form or request. The server validates it before processing.

<!-- Form with proper CSRF token -->
<form action="/transfer" method="POST">
  <input type="hidden" name="csrf_token" value="a8f3d2...randomly generated...">
  <input type="text" name="amount">
  <button type="submit">Transfer</button>
</form>

The attacker can't read the token from your page (same-origin policy prevents it), and they can't guess it (it's cryptographically random), so they can't forge a valid request.

SameSite cookies

Modern browsers support the SameSite cookie attribute, which controls whether cookies are sent on cross-site requests:

SameSite=Laxas a browser default has significantly reduced CSRF exposure, but it's not a complete fix. GET-based state changes are still exploitable, and top-level navigation attacks (a link that triggers a GET-based action) still work.

Don't rely solely on SameSite cookies as CSRF protection. Some browsers handle it differently, some users are on older browsers, and the Lax default still allows certain cross-site request types. Implement CSRF tokens for all state-changing endpoints.

CSRF in the wild

CSRF vulnerabilities consistently appear in bug bounty programmes, particularly on:

Impact scales with what the vulnerable action does. A CSRF against a log-out endpoint is low severity. A CSRF against a password-change or fund-transfer endpoint is critical.

// Practice this

Everything in this post has a live lab on hackr.gg. Spin up a vulnerable machine and exploit it yourself — no setup, no VPN, runs in your browser.

Open CSRF Attacks course →
More posts
Web Security
SQL Injection: How One Quote Character Breaks a Database
9 min
Web Security
XSS: From alert(1) to Session Hijack
11 min
Career
How to Start Bug Bounty With Zero Experience (Realistic Guide)
14 min