Beginner levelFree

CSRF (Cross-Site Request Forgery) — fully explained with bypasses

CSRF: how it's exploited, common defenses (tokens, SameSite, Origin), bypasses (method change, JSON, double-submit, content-type) and where to hunt it in any app.

Gorka El BochiMay 9, 202612 min

Quick answer

CSRF (Cross-Site Request Forgery) forces a user to perform unwanted actions on an app where they're authenticated. It targets state-changing requests (change email, transfer money) mounted from another domain. It's mitigated with CSRF tokens validated on the server, SameSite cookies, Origin/Referer validation, and rejecting unexpected Content-Types. It breaks when the developer validates any of those defenses poorly.


How the attack works

CSRF exploits the fact that the browser automatically attaches cookies to cross-site requests. If the victim is logged into bank.tld and visits attacker.tld, an attacker's page can make the browser send a POST to bank.tld/transfer with the session cookie included.

If the server only validates the cookie, the transfer executes as if the victim had initiated it. That's CSRF.


Common defenses (and how they break)

1. CSRF Tokens

The server injects a random token into every form. The client sends it in every request. If it doesn't match → 403.

Typical bypasses:

  • Token absent. Remove the parameter from the request → does the server still accept it? Some backends only validate when it's present.
  • Empty token. csrf_token= (empty string) → some validators accept it.
  • Weak token. Is it a fixed value per account? Is it reused? Capture yours, build the PoC for the victim with your token.
  • Only part of the token validated. If only half or a prefix is validated, send any token with that valid prefix.
  • Token expires but isn't invalidated. If reusing old tokens works → CSRF.

A cookie attribute that controls when it's sent cross-site:

  • SameSite=Strict — never cross-site. Kills CSRF at the root.
  • SameSite=Lax (current default in Chrome/Firefox) — only on top-level navigation (link click). NOT on cross-site POST → blocks classic CSRF.
  • SameSite=None; Secure — always sent. Vulnerable.

Bypasses:

  • If the app uses SameSite=Lax, GET requests do cross. If a critical action accepts GET (/password_change?new=...), CSRF is possible.
  • Legacy apps with cookies that don't declare SameSite behave as None in some browsers.

3. Origin / Referer Header

The server compares the Origin (or Referer) header with its own domain.

Bypasses:

  • Remove Referer. <meta name="referrer" content="no-referrer"> on the attacker page. If the server "lets it through" when there's no Referer → bypass.
  • Partial spoofing. Referer: example.tld.attacker.tld (a subdomain that contains the target domain as a prefix). If the check is startsWith() or contains(), it passes.
  • Origin null. Sandboxed iframes send Origin: null → if the server accepts it, bypass.

4. Content-Type validation (for JSON APIs)

If the endpoint requires Content-Type: application/json, it's not trivial to mount it in a classic HTML form. But:

  • A form with enctype="text/plain" can generate JSON-shaped bodies:
html
<form action="https://target.tld/api/email" method="POST" enctype="text/plain">
  <input name='{"email":"atk@evil.tld","x":"' value='"}'>
</form>

The resulting body: {"email":"atk@evil.tld","x":"="} — valid JSON if the server parses it.

  • Change the Content-Type. If the API also accepts application/x-www-form-urlencoded in addition to JSON → simple form CSRF.
  • Plain/text. Some backends ignore the Content-Type and parse the body directly.

Bypass in JSON requests

Real example:

html
<html>
  <head><meta name="referrer" content="unsafe-url"></head>
  <body>
  <form name="hacker" method="POST" action="https://target.tld/api/phone.json" enctype="text/plain">
    <input type="hidden" name='{"phone":"+34666000000","a":"' value='"}'>
  </form>
  <script>
    document.forms[0].submit();
  </script>
  </body>
</html>

Body sent: {"phone":"+34666000000","a":"="}. If the server parses it as JSON, the attack succeeds.


CSRF + ClickJacking

If the server blocks CSRF but the page can be embedded in an <iframe>, you mount a clickjacking:

html
<iframe src="https://target.tld/settings/password" width="500" height="500"></iframe>

By positioning the iframe over a visible attacker button, the click lands on the target iframe. The browser attaches cookies + the server processes the action.

Mitigation: X-Frame-Options: DENY or CSP frame-ancestors 'none'.


Endpoints where to hunt CSRF

  • Email change (leads to ATO if the user doesn't re-confirm).
  • Password change.
  • 2FA settings change (disabling 2FA → ATO).
  • Adding SSH keys / API keys to the profile.
  • Transfers / payments / subscriptions.
  • Account deletion (defacement vector).
  • Linking OAuth/SSO accounts (can lead to takeover if the victim links the attacker's account).
  • Role / permission changes in apps with an admin panel.

Pattern: CSRF relay endpoints

Some apps have internal endpoints like /api/proxy?url=/api/endpoint that forward internally and automatically inject the user's CSRF token. For the attacker, all it takes is for the victim to visit the URL — they don't need to steal the token.

Look for endpoints named proxy, redirect, forward, relay, dialog, _DONOTUSE, _DEBUG, _INTERNAL. If they forward authenticated requests → possible universal CSRF.


Hunting checklist

  • Does the server accept the request without a CSRF token?
  • Does an empty token pass? Does another account's token pass?
  • Are there sensitive endpoints that accept GET? (check methodOverride too).
  • Is SameSite declared? Is it Lax/None?
  • Is Origin/Referer validated strictly or just "contains"?
  • Is the Content-Type validated or ignored?
  • Is there a relay/proxy endpoint that injects the token automatically?
  • Is X-Frame-Options present, to rule out clickjacking?

Practice CSRF + bypasses with real apps that validate tokens, Origin and Content-Type: CSRF labs.

Practice this in a lab

Csrf

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account

Related articles

hunters training
711

hunters training

labs from real reports
55

labs from real reports

completions
1,205

completions

in bounties practiced
$213,970

in bounties practiced

46 flags captured this week·Real reports from HackerOne · Bugcrowd · Intigriti·No commitment·Free Academy