Cross-site request forgery (CSRF) abuses the fact that browsers can automatically attach a user’s cookies to requests. If a state-changing endpoint trusts only the presence of an authenticated cookie, another site may be able to trigger that endpoint from the user’s browser.

Modern cookie controls reduce the attack surface, but robust applications still need to reason about request semantics and trust boundaries.

Understand the condition that makes CSRF possible

A typical CSRF attack needs three ingredients:

  1. the victim is authenticated to the target site;
  2. the browser automatically attaches authentication state, usually cookies;
  3. an attacker can cause the browser to send a state-changing request.

The attacker does not need to read the response. Triggering an action can be enough.

This is why endpoints such as changing an email address, adding a payment destination, or deleting data must not rely only on ambient cookie authentication.

Keep safe and unsafe HTTP methods distinct

GET, HEAD, and other safe methods should not perform state-changing actions.

Do not build endpoints such as:

GET /account/delete?id=42

A link, image, prefetcher, crawler, or cross-site navigation could trigger it unexpectedly.

Use state-changing methods such as POST, PUT, PATCH, or DELETE for mutations, then apply CSRF protections to those requests.

Use SameSite as a strong first layer

A session cookie can declare a SameSite policy:

Set-Cookie: session=...; Secure; HttpOnly; SameSite=Lax

SameSite=Lax blocks many cross-site cookie sends while preserving common top-level navigation behavior. SameSite=Strict is more restrictive and can be appropriate where cross-site entry flows do not need the cookie.

SameSite=None allows cross-site cookie use and requires Secure; choose it only when the application genuinely needs cross-site contexts.

SameSite is valuable, but it should be treated as one layer. Application architecture, legacy clients, subdomain relationships, and browser behavior can create cases where an explicit request token is still appropriate.

Add an unpredictable anti-CSRF token

A common pattern is to generate a cryptographically random token associated with the user’s session and require it in each unsafe request.

For an HTML form:

<input type="hidden" name="csrf_token" value="RANDOM_SESSION_TOKEN">

For JavaScript API requests, the token can be sent in a custom header:

X-CSRF-Token: RANDOM_SESSION_TOKEN

The server compares the submitted token with the value associated with the authenticated session.

An attacker can often cause the browser to send a form, but the attacker’s site should not know a secret token issued to the target site’s page.

Generate tokens correctly

CSRF tokens should be unpredictable. Use a cryptographically secure random generator provided by the platform.

Do not derive tokens from usernames, timestamps, sequential IDs, or other guessable values.

Tokens do not need to contain user secrets. They need enough entropy to make forgery impractical and a reliable association with the expected session or request context.

Some stateless architectures use a token stored in both a cookie and a request field or header. The server checks that the two values match.

This can work when implemented with appropriate signing or binding, but a naive design can be weakened if an attacker can plant cookies through a related subdomain or another mechanism.

Session-bound server-side tokens are often simpler to reason about when server-side session state already exists.

Validate Origin or Referer as defense in depth

For unsafe requests, servers can inspect the Origin header and require it to match an allowed origin.

Conceptually:

if request.method is unsafe:
    require Origin == https://example.com

Origin checks are especially useful for API endpoints. Depending on clients and request types, fallback handling for Referer may be needed.

Do not accept substring matches such as example.com.attacker.test. Parse and compare normalized scheme, host, and port against an explicit allowlist.

CORS is not a complete CSRF defense

Cross-Origin Resource Sharing controls whether browser scripts can read or make certain cross-origin requests with specific headers and methods. It does not mean every cross-site request is impossible.

Simple forms and navigations can still send requests that do not require a CORS preflight.

Configure CORS narrowly, but protect cookie-authenticated mutations independently.

Be cautious with subdomains

Organizations often treat *.example.com as one operational family, but a less-trusted subdomain can affect assumptions about cookies and same-site behavior.

If arbitrary users can host content on a subdomain, do not automatically treat every same-site request as trusted. Keep sensitive applications on carefully controlled origins and scope cookies as narrowly as practical.

APIs using bearer tokens have a different risk model

If authentication uses an Authorization: Bearer ... header that browser code must add explicitly, classic cookie-based CSRF is less direct because the browser does not attach that header automatically to an attacker’s form submission.

That does not make the API secure by itself. Token theft, XSS, CORS mistakes, and insecure storage remain important threats.

Choose CSRF controls based on how authentication credentials reach the server.

Common pitfalls

Using SameSite=None everywhere

Cross-site cookies expand the CSRF surface. Use the least permissive setting compatible with product requirements.

Putting CSRF tokens in URLs

URLs can leak through logs, browser history, analytics, and referrer data. Send tokens in form bodies or headers.

Allowing state changes over GET

Safe methods should remain safe. CSRF defenses are stronger when HTTP semantics are clean.

Comparing origins loosely

Parse origins and compare against exact trusted values rather than suffix or substring checks.

Forgetting that XSS changes the threat model

A script executing in your origin can often read CSRF tokens and issue valid requests. Preventing cross-site forgery does not replace output encoding, Content Security Policy, and other XSS defenses.

Layer the controls

A practical cookie-authenticated application can combine:

  1. Secure and HttpOnly session cookies;
  2. an appropriate SameSite policy;
  3. unsafe methods for mutations;
  4. anti-CSRF tokens for state-changing requests;
  5. strict Origin validation where practical;
  6. narrow CORS configuration;
  7. tests that simulate missing, invalid, and cross-origin requests.

CSRF defense works best when no single browser behavior is responsible for security. Make mutations explicit, require evidence that the request originated from your application, and keep authentication cookies as narrowly scoped as the product allows.