CSRF (Cross-Site Request Forgery) is a class of attack in which a malicious website tricks the victim's browser into sending, without their consent, an authenticated request to an application they are already logged into. The result is a state-changing action — transferring money, changing an email, altering permissions — executed in the name of the legitimate user but orchestrated by the attacker. In this Knowledge article we explain, in depth and with examples, how CSRF works, its impact and which defenses genuinely solve the problem in web applications, APIs and SPAs.

What is CSRF

CSRF exploits one of the web's trust assumptions: the browser automatically attaches a domain's session cookies to every request destined for that domain, regardless of which page originated the request. If a user is authenticated on bank.example.com and, in another tab, opens a site controlled by the attacker, that site can fire off a request to bank.example.com. The browser dutifully sends the valid session cookie along with it — and the server, unaware that the intent did not come from the user, processes the action as legitimate.

The vulnerability is not about stealing credentials or intercepting traffic. The attacker never actually sees the victim's cookie; they merely use it indirectly, through the browser. This is why CSRF is also known as session riding: the attack rides on an already established session and steers it wherever the attacker wants.

The cataloged pattern is CWE-352: Cross-Site Request Forgery, and the topic is covered in depth by the community in the OWASP CSRF Prevention Cheat Sheet. Historically, CSRF was its own category in the OWASP Top 10 (A8:2013); in recent editions it was absorbed into the Broken Access Control category, reflecting that it is, at its core, a failure to enforce access control over the user's intent.

How the attack works

For a CSRF attack to succeed, three conditions are usually present at the same time:

  • A relevant action: there is an operation that changes state and is of interest to the attacker (changing an email, transferring funds, promoting a user to administrator).
  • Cookie-based authentication: the session is managed solely by cookies that the browser sends automatically, with no additional secret the attacker cannot predict.
  • Predictable parameters: the attacker can build the entire request in advance, because there are no unpredictable values (such as a token) they would need to guess.

Consider a naive email-change endpoint that accepts a simple POST:

POST /account/email HTTP/1.1
Host: app.example.com
Cookie: session=abc123...
Content-Type: application/x-www-form-urlencoded

[email protected]

The attacker hosts on their own site a page that reproduces exactly this request. A self-submitting form is enough:

<form action="https://app.example.com/account/email" method="POST">
  <input type="hidden" name="new_email" value="[email protected]">
</form>
<script>document.forms[0].submit();</script>

When the authenticated victim visits that page — via a link in an email, an image on a forum, a malicious ad — the form fires. The browser sends the POST to app.example.com with the valid session cookie attached, and the account's recovery email becomes the attacker's. Next, they request "forgot my password" and take over the account.

GET requests that change state are even easier to exploit: all it takes is an <img src="https://app.example.com/transfer?to=attacker&amount=1000"> tag embedded in any page. The browser loads the "image" and the transfer happens silently. This is one of the reasons state-changing operations should never be exposed via GET.

Impact: actions without consent

The impact of CSRF is always the execution of a state-changing action without the user's consent, but with their authority. The severity scales with the power of the compromised account and the criticality of the endpoint reached:

  • Account takeover: changing an email or password, adding a recovery key, registering a trusted device.
  • Financial fraud: transfers, payments, altering the banking details for incoming funds.
  • Privilege escalation: in an admin panel, forcing a logged-in admin to create a new administrator user or disable security controls.
  • Configuration changes: altering firewall rules, email routes, integrations and webhooks — compromising the entire organization.

When the victim is a privileged account, a single successful CSRF can serve as a springboard to compromise the entire environment. That is why risk should not be measured only by the "simplicity" of the endpoint, but by what the action allows in the business context.

Effective defenses against CSRF

There is no single silver bullet; robust protection combines layers. The most important are described below and summarized in the table at the end of this section.

Anti-CSRF tokens

The canonical defense is the anti-CSRF token: an unpredictable value, generated by the server, that must accompany every state-changing request. Because the attacker cannot read or guess this token (the browser's same-origin policy prevents them from reading responses from another domain), they cannot build a valid request. There are two main patterns:

Synchronizer Token Pattern
The server generates a unique token per session (or per request), stores it server-side and inserts it into a hidden field of each form. On each submission, the server compares the received token with the stored one. It is the strongest pattern, recommended for applications with server-side sessions.
Double-Submit Cookie
The server issues the token in a cookie and the application also resends it in a header or form field. The server merely checks that the two values match, without having to keep state. It is useful for stateless architectures with no server-side session, provided the token is signed/encrypted to prevent fixation via subdomains.

Practical rules: the token must be cryptographically random, have sufficient entropy, be tied to the user's session and validated on all requests that change state (POST, PUT, PATCH, DELETE). Never send the token via GET in the URL, where it can leak through logs, history and the Referer header.

SameSite cookies

The SameSite attribute instructs the browser not to send the cookie on requests originated by another site, attacking the root of the problem. There are three values:

  • SameSite=Strict: the cookie is never sent in cross-site contexts, not even when following a link from another domain. Maximum protection, but it can hurt the experience (the user arrives logged out after clicking an external link).
  • SameSite=Lax: the cookie is sent only on top-level navigations with safe methods (such as a GET when clicking a link), but not on cross-site POST requests nor on background requests. It is the default adopted by modern browsers and offers a good balance.
  • SameSite=None: sends the cookie in any context (requires Secure). Reserved for legitimate cross-site use cases — which, for that very reason, need other defenses.

SameSite=Lax as the browser default has mitigated much of the classic attacks, but it does not replace tokens: there are older browsers and clients without this behavior, and Lax does not protect actions triggered by a top-level navigation with a state-changing GET. Treat SameSite as defense in depth, not as the only control.

Origin and Referer verification

For state-changing requests, the server can validate the Origin and Referer headers, rejecting anything that does not belong to a list of trusted origins. The Origin header is sent on state-changing requests and is hard to forge via JavaScript. It is a cheap and valuable check as an additional layer, though it requires handling cases where the header is absent (proxies, privacy policies).

Re-authentication for sensitive actions

For high-impact operations — changing a password, altering the recovery email, authorizing a large payment, changing banking details — require an additional proof of intent: re-entering the password, confirmation via a second factor (MFA) or step-up authentication. Because the attacker does not have this momentary secret, the forged request fails even if all other defenses were to fall.

DefenseHow it protectsWhen to use
Synchronizer TokenSecret per-session token validated on the serverApps with server-side sessions; default protection for forms
Double-Submit CookieCompares token in cookie vs. header/fieldStateless architectures; APIs with no session state
SameSite=LaxBrowser does not send cookie on cross-site POSTBaseline across the application; defense in depth
SameSite=StrictCookie never leaves in cross-site contextSession cookies for apps with no external-link flow
Origin/RefererRejects requests from untrusted originsAdditional layer on state-changing endpoints
Re-authentication / MFARequires a momentary secret the attacker lacksCritical actions: password, email, payment, privileges
Custom header (SPA/API)Header that only same-origin JS can sendAPIs consumed by SPAs via fetch/XHR

CSRF in APIs and SPAs

Modern applications frequently separate the frontend (SPA) from the backend (API). The CSRF risk here depends on how authentication is transported:

  • Cookie-based session: if the API authenticates via a cookie sent automatically by the browser, it remains exposed to CSRF and needs the same defenses — tokens, SameSite and Origin verification.
  • Token in a header (Bearer): if the client sends the authentication token explicitly in an Authorization header (stored, for example, in the SPA's memory), the browser does not attach it by itself to cross-site requests — which, in practice, removes the classic CSRF vector. The price is having to protect that token against theft via XSS.

A common technique for APIs consumed by SPAs is to require a custom header (for example, X-Requested-With or a proprietary header). Because custom headers can only be added via same-origin JavaScript — and cross-site requests with non-simple headers trigger a CORS preflight — the attacker cannot reproduce them from another domain. Configure CORS restrictively: do not use Access-Control-Allow-Origin: * combined with credentials, and validate the list of allowed origins.

Relationship with XSS

CSRF and XSS are distinct flaws, but they feed off each other. CSRF exploits the trust the server has in the victim's browser; XSS exploits the trust the browser has in the code delivered by the site. The critical point is this: any anti-CSRF defense is nullified by an XSS on the same origin. If the attacker can execute JavaScript in your application's context, they read the anti-CSRF token, forge the custom header and ignore SameSite, because the requests now come from the legitimate origin itself. That is why addressing XSS is not optional: without it, the CSRF protection model is only partial. Combine output sanitization, a Content Security Policy and HttpOnly cookies with the CSRF defenses described here.

References

How Decripte helps your company

CSRF flaws often slip past hurried reviews because they do not obviously "leak data" — the damage shows up when an account is hijacked or a critical setting is changed. At Decripte, we cover the OWASP Top 10 attack surface end to end: code and architecture review, penetration testing of web applications and APIs, and continuous monitoring of the security posture of organizations of any size — from 1 to more than 100,000 employees. We help define where to apply tokens, how to configure SameSite and where to require re-authentication, without sacrificing the user experience. You can start for free by assessing the risk of your attack surface and, when you want to move on to continuous monitoring, explore our plans designed for your size.