What is Cross-Site Request Forgery (CSRF)?

Cross-Site Request Forgery (CSRF) is an attack that tricks a user's browser into making an unintended request to a web application where the user is already authenticated. The browser automatically attaches cookies -- including session cookies -- to every request sent to a domain, regardless of which site initiated the request. CSRF exploits this fundamental behavior of the web platform.

Unlike cross-site scripting (XSS), where the attacker injects code that runs in the context of the victim's session, CSRF does not require injecting any code into the target application. The attacker never sees the response. They only need to trigger a state-changing request -- a bank transfer, a password change, an email forwarding rule -- and the browser does the rest.

How Browsers Handle Cookies

To understand CSRF, you need to understand how browsers handle cookies. When a server sets a cookie with Set-Cookie: session=abc123, the browser stores it and attaches it to every subsequent request to that domain. It does not matter whether the request was initiated by the user clicking a link on the target site, by a form on a completely different site, or by an image tag embedded in a malicious email.

User Browser evil.com bank.com 1. visits 2. hidden form POST /transfer to=attacker&amt=5000 Cookie: session=abc123 3. request + cookies 4. valid session cookie transfer executed! The browser sends the session cookie automatically. bank.com cannot distinguish this from a legitimate request. The attacker never sees the response -- they only need the side effect (the money transfer) to succeed.

This is the core of CSRF: the browser cannot distinguish between a request the user intended to make and one that was triggered by a malicious page. The session cookie is attached either way, and the server sees a fully authenticated request.

Attack Scenarios

CSRF attacks target state-changing operations -- actions that modify data on the server. They cannot be used to steal data because the attacker never sees the response. The most dangerous CSRF targets are operations with high-value side effects.

Money Transfers

A banking application with a transfer endpoint at POST /transfer that accepts to and amount parameters is a textbook CSRF target. The attacker embeds a hidden form on their page that auto-submits when the victim visits:

<form action="https://bank.com/transfer" method="POST" id="f">
  <input type="hidden" name="to" value="attacker-account"/>
  <input type="hidden" name="amount" value="5000"/>
</form>
<script>document.getElementById('f').submit();</script>

If the user is logged into bank.com in another tab, the browser sends the session cookie with the form submission. The bank's server sees a valid authenticated request and executes the transfer.

Password Changes

If a password change form does not require the current password (or relies solely on the session cookie for authentication), an attacker can force a password change. After the victim's password is changed, the attacker logs in with the new password and takes over the account entirely.

Email Forwarding Rules

This is particularly insidious. An attacker uses CSRF to add a forwarding rule to the victim's email account, routing a copy of all incoming mail to the attacker's address. Unlike a password change, the victim may not notice this for weeks or months, during which time the attacker silently collects every email -- including password reset links for other services.

Admin Actions

CSRF against admin accounts is especially dangerous. If an administrator visits a page with a CSRF payload, the attack can create new admin users, change application settings, exfiltrate database backups, or modify access controls. The attacker does not need admin credentials -- they only need the admin to visit a page while logged in.

GET vs POST CSRF

CSRF is possible with both GET and POST requests, but the mechanics differ.

GET-based CSRF

If a state-changing operation can be triggered via GET (which violates HTTP semantics -- GET should be safe and idempotent), the attack is trivial. The attacker can use an image tag, a script tag, or a simple link:

<img src="https://bank.com/transfer?to=attacker&amount=5000" width="0" height="0"/>

The browser loads the "image" by making a GET request to bank.com, sending cookies along. This works even in emails rendered in webmail clients, making GET-based CSRF extremely dangerous. This is why HTTP standards require that GET requests must never have side effects.

POST-based CSRF

Most state-changing operations use POST, which requires a form submission. The attacker creates a hidden form and auto-submits it with JavaScript. Cross-origin POST requests with simple content types (application/x-www-form-urlencoded, multipart/form-data, text/plain) do not trigger a CORS preflight, so the browser sends them freely -- cookies included.

Requests with non-simple content types like application/json trigger a CORS preflight OPTIONS request first. If the server does not respond with appropriate CORS headers, the browser blocks the request. This is an incidental defense, not an intentional one, and should not be relied upon as the sole CSRF protection.

Real-World CSRF Attacks

Netflix CSRF (2006)

In 2006, security researcher Jeremiah Grossman demonstrated a CSRF attack against Netflix that could add DVDs to a victim's queue, change the shipping address, and modify account credentials -- all by having the victim visit a malicious webpage while logged into Netflix. At the time, Netflix had no CSRF protections. The attack demonstrated that CSRF was not merely a theoretical concern but a practical threat against major web applications.

ING Direct CSRF (2008)

ING Direct, one of the largest online banks, was found to be vulnerable to CSRF that could transfer funds between a victim's accounts. The attack was more sophisticated: it first used CSRF to create a new payee, then initiated a transfer to the attacker-controlled payee. The multi-step nature of the attack showed that workflows requiring multiple requests are not inherently safe from CSRF.

Gmail Filter CSRF (2007)

A CSRF vulnerability in Gmail allowed attackers to create email filters on a victim's account. The attacker could set up a filter that automatically forwarded all emails matching certain criteria (or all emails) to an external address. Victims who visited a malicious site while logged into Gmail had their email silently forwarded without any visible indication.

Defense: Synchronizer Token Pattern

The most widely deployed CSRF defense is the synchronizer token pattern (also called anti-CSRF tokens). The server generates a cryptographically random token, associates it with the user's session, and embeds it in every form as a hidden field. When the form is submitted, the server verifies that the token matches.

Browser Server 1. GET /transfer-form 2. HTML + hidden csrf_token=x7f9... <input type="hidden" name="csrf_token" value="x7f9"> <input name="amount" ...> 3. POST /transfer csrf_token=x7f9 Cookie: session=abc123 4. token matches session? YES --> allow request Attacker's form cannot include the token -- they don't know its value. Cross-origin pages can't read it.

The attacker cannot include the correct token in their forged request because they cannot read the target page's HTML from a cross-origin context. The same-origin policy prevents evil.com from reading the contents of pages served by bank.com, so the token value remains secret.

For this defense to work, several conditions must hold:

Defense: Double-Submit Cookie

The double-submit cookie pattern is a stateless alternative. The server sets a random value in both a cookie and a request parameter (header or form field). On submission, the server checks that the cookie value matches the parameter value.

This works because an attacker can cause the browser to send a cookie but cannot read the cookie value from a different origin. They cannot set the matching request parameter without knowing the cookie's value. However, this approach is weaker than synchronizer tokens if the attacker can perform a subdomain takeover or cookie injection attack -- they could set or overwrite the CSRF cookie from a related subdomain.

Defense: SameSite Cookie Attribute

The SameSite cookie attribute, first shipped in Chrome 51 (2016) and now supported by all major browsers, gives servers direct control over when cookies are sent on cross-site requests. It has three values:

Since Chrome 80 (February 2020), cookies without an explicit SameSite attribute default to Lax. This single change eliminated a large class of CSRF vulnerabilities across the web, protecting applications that had never implemented any CSRF defenses.

Defense: Origin and Referer Header Checking

When a browser sends a request, it includes an Origin header (on POST, PUT, DELETE, and CORS requests) and a Referer header (on most requests) indicating where the request came from. The server can reject requests where the origin does not match the expected domain.

For example, if bank.com receives a POST request with Origin: https://evil.com, it knows the request was initiated cross-site and can reject it. This is a useful defense-in-depth measure, but it has limitations:

For these reasons, origin checking is best used as a supplementary defense alongside tokens or SameSite cookies, not as the sole protection.

Defense: CORS and Preflight Requests

Cross-Origin Resource Sharing (CORS) is not a CSRF defense per se, but it intersects with CSRF in important ways. When JavaScript makes a cross-origin request with a non-simple content type (such as application/json), the browser sends a preflight OPTIONS request first. If the server does not respond with the appropriate Access-Control-Allow-Origin header, the browser blocks the actual request.

Browser API Server OPTIONS /api/transfer Origin: https://evil.com No Access-Control-Allow-Origin Browser blocks the actual request WARNING: Simple POST requests (form-urlencoded) skip preflight. CORS protects JSON APIs but not traditional form submissions. Do not rely on CORS as your only CSRF defense.

This means JSON APIs that require Content-Type: application/json are incidentally protected from cross-origin form submissions. However, simple form submissions (application/x-www-form-urlencoded) bypass CORS preflight entirely. An attacker can craft a form that submits to any URL with a simple content type, and the browser will send it -- cookies and all -- without any preflight check.

How SPAs and JWTs Changed the Landscape

Single-page applications (SPAs) that communicate with backend APIs via fetch() or XMLHttpRequest have a fundamentally different CSRF profile than traditional server-rendered applications.

Token-Based Authentication

Many modern SPAs use JSON Web Tokens (JWTs) stored in localStorage or sessionStorage instead of cookies. The token is sent in the Authorization: Bearer <token> header on each request. Since the token is not in a cookie, the browser does not attach it automatically to cross-origin requests. This makes the application immune to CSRF by default -- the attacker cannot forge the Authorization header from a cross-site page.

However, if JWTs are stored in cookies (which some frameworks do to simplify token management and enable server-side rendering), all the same CSRF risks apply. The storage location, not the token format, determines CSRF vulnerability.

SPA API Patterns

SPAs typically send Content-Type: application/json requests, which trigger CORS preflight. Combined with strict CORS configuration that only allows the application's own origin, this provides a strong layer of CSRF protection. An attacker's page at evil.com cannot make a fetch() call with Content-Type: application/json to api.bank.com without a successful preflight.

But developers must be careful: if the API also accepts application/x-www-form-urlencoded for backward compatibility, the CORS protection is bypassed for those endpoints.

OAuth 2.0 and CSRF

OAuth 2.0 flows are themselves susceptible to a form of CSRF. The authorization code redirect can be forged: an attacker initiates an OAuth flow with their own authorization code, then tricks the victim's browser into completing the callback. This binds the attacker's third-party account to the victim's session. The state parameter in OAuth 2.0 exists specifically to prevent this -- it functions as a CSRF token for the OAuth redirect.

Defense in Depth: Layered Approach

No single defense is sufficient on its own. A robust anti-CSRF strategy layers multiple protections:

Defense in Depth Layer 1: SameSite=Lax cookies (default in modern browsers) Layer 2: CSRF tokens (synchronizer or double-submit) Layer 3: Origin / Referer header validation Layer 4: Re-authentication for sensitive ops Each layer defends against failures in the layers above it.
  1. SameSite cookies as the baseline -- they stop most CSRF with zero application code changes
  2. CSRF tokens for state-changing endpoints -- defense against browsers that do not fully support SameSite, or against subdomain-based attacks
  3. Origin/Referer checking as a backstop -- catches cases where tokens might be accidentally omitted
  4. Re-authentication for high-value operations -- password changes, large transfers, and admin actions should require the user to confirm their identity, regardless of other protections

Common Implementation Mistakes

Even when developers implement CSRF protections, common mistakes undermine them:

CSRF in the Modern Web

The CSRF threat has evolved significantly. The combination of SameSite=Lax as the browser default, widespread adoption of token-based authentication in SPAs, and better framework support for anti-CSRF tokens has reduced the prevalence of CSRF vulnerabilities. Many modern web frameworks -- Django, Rails, Laravel, Spring Security, ASP.NET -- generate and validate CSRF tokens automatically for form submissions.

However, CSRF has not disappeared. Applications that need SameSite=None for legitimate cross-site functionality (embedded widgets, federated login, payment processors) remain vulnerable without additional defenses. Legacy applications running behind reverse proxies may not benefit from SameSite defaults. And as the web platform evolves, new cross-origin interaction patterns may introduce novel CSRF-like attacks.

Understanding CSRF remains essential for anyone working on web security. The attack exploits a fundamental design decision of the web platform -- that browsers automatically attach credentials to requests -- and defending against it requires understanding how that decision ripples through every layer of the stack, from cookie handling to CORS to TLS and authentication protocol design.

Try It: Explore the Network Infrastructure

CSRF protection depends on the security infrastructure of the web -- TLS for secure cookies, DNS for origin verification, and the network layer that carries it all. Explore how these systems work at the routing level:

See BGP routing data in real time

Open Looking Glass
More Articles
How TLS/HTTPS Works: Securing the Internet's Traffic
Certificate Transparency: How CT Logs Secure the Web's PKI
How Firewalls Work: Packet Filtering, Stateful Inspection, and Beyond
What is Cross-Site Scripting (XSS)?
What is Server-Side Request Forgery (SSRF)?
What is SQL Injection?