CSRF (Cross-Site Request Forgery)
About 3 min read
Last updated: 2026-09-01
What Is CSRF (Cross-Site Request Forgery)
CSRF (Cross-Site Request Forgery) is an attack technique that causes a user's browser to send unintended requests to a web site where the user is already authenticated. The attacker prepares a trap web page or email, and the moment the victim views it, a malicious request is automatically sent from the victim's browser to the target site.
Browsers automatically attach cookies that belong to the destination domain regardless of which page the request originated from, so if the victim is logged in, the attacker's request is processed as authenticated. Operations that change state - such as password changes, money transfers, email address changes, and purchases - become targets of the attack.
How the Attack Works with Examples
A CSRF attack is executed in the following flow.
- The victim logs into the target site (e.g., online banking), and a session cookie is stored in the browser
- The victim views a trap page prepared by the attacker (via an email link, forum post, etc.)
- HTML or JavaScript embedded in the trap page sends a request from the victim's browser to the target site
- The browser automatically attaches the session cookie, so the target site processes it as a legitimate request
For example, if a transfer function is designed so that POST /transfer takes the recipient and the amount from the request body, the attacker only needs to embed an auto-submitting form in the trap page to execute a transfer from the victim's account. If the form is placed in an off-screen iframe and submitted with JavaScript, the victim never notices that the operation happened.
While often confused with XSS, CSRF is fundamentally different in that it forges legitimate requests from the victim's browser rather than executing the attacker's script in the victim's browser.
Implementing Defenses
The core of CSRF defense is implementing mechanisms that verify whether a request is based on the legitimate user's intent.
CSRF Tokens
The server generates a random token when displaying a form and embeds it in a hidden field. When the form is submitted, the server verifies that the token matches, rejecting forged requests from external sites. This approach is called the Synchronizer Token Pattern: the token is unique per session, and the same-origin policy keeps JavaScript on external sites from reading it. If XSS exists, however, the token itself can be stolen, so CSRF tokens only work when combined with XSS countermeasures. When you do not want to hold tokens on the server, the double-submit cookie pattern, which places a signed value in both a cookie and the request and compares the two, is an alternative.
SameSite Cookie Attribute
Setting the SameSite attribute on cookies controls whether cookies are attached to cross-site requests.
SameSite=Strict: Does not attach cookies to any requests from external sites. Most secure, but login state is not maintained when accessing via external linksSameSite=Lax: Attaches cookies only to GET requests from top-level navigation (link clicks). Since cookies are not attached to POST requests, most CSRF attacks are prevented. When the attribute is unspecified and Lax is applied as the default, however, a more permissive version is used and cookies are attached to POST requests made within two minutes of the cookie being setSameSite=None: Attaches cookies to cross-site requests as well. Must be used with theSecureattribute
The SameSite attribute is effective as defense in depth, but it is not a CSRF countermeasure on its own. Same-site is judged at the registrable domain level, so a cookie issued by app.example.com is treated as same-site even for requests from other.example.com, which turns a hijacked subdomain or third-party content on a shared domain into a bypass. Cookies that have to specify SameSite=None for external service integration also remain, as do older browsers and embedded browsers where the default is not applied, so always combine SameSite with token verification.
Other Countermeasures
- Origin / Referer header verification: Confirm that the request origin is your own site. However, the Referer may not be sent due to privacy settings or proxies
- Requiring custom headers: HTML forms cannot add arbitrary headers, so requiring a custom header such as
X-Requested-Withrejects forged requests sent through forms. Adding such a header from cross-site JavaScript requires a CORS preflight, but this defense does not hold if you widen the allowed origins whileAccess-Control-Allow-Credentialsis enabled, so keep the allowlist as narrow as possible - Dividing roles with security headers: CSP and
X-Frame-Optionsare not mechanisms that stop CSRF itself. Defend against CSRF with cookie attachment conditions and request origin verification, and design security headers as the means of closing the separate paths of XSS and clickjacking
Built-in CSRF Protection in Frameworks
Many server-side web frameworks provide built-in CSRF protection.
- Django:
CsrfViewMiddlewareis enabled by default and verifies the token that the{% csrf_token %}template tag embeds. Over HTTPS it also checks theOriginheader againstCSRF_TRUSTED_ORIGINS, which covers attacks that come through subdomains - Ruby on Rails: In newly created applications
config.action_controller.default_protect_from_forgeryis enabled, so CSRF tokens are generated and verified automatically. To set it explicitly, writeprotect_from_forgery with: :exception - Spring Security: CSRF protection applies by default to unsafe HTTP methods such as POST. When you use the Thymeleaf or JSP integration,
CsrfTokenis inserted into forms automatically - Next.js / SPA: For API routes that use cookie authentication, combine SameSite cookies with
Originheader verification. Consider additional protection with a WAF
If you disable a framework's CSRF protection, verify that alternative countermeasures are reliably implemented. When excluding CSRF protection for API endpoints, the prerequisite is switching to Bearer Token authentication and not using cookie-based authentication.
To learn more about this topic, see HTTP Security Headers: 5 Essential Headers to Protect Your Website.
Common Misconceptions
- CSRF cannot occur with GET requests
- If state-changing operations (deletion, settings changes, etc.) are implemented via GET requests, they become CSRF targets. A GET request can be sent simply by setting a URL in an img tag's src attribute, so state changes should always be implemented with POST/PUT/DELETE. Even after <code>SameSite=Lax</code> became the default, Lax lets GET through as a safe method, so implementations that change state via GET are not protected by the browser default.
- Using HTTPS prevents CSRF
- HTTPS provides communication encryption and tamper prevention but is unrelated to CSRF. CSRF sends legitimate requests from the legitimate user's browser, so the attack succeeds even when communication is encrypted.