Understanding CSRF Attacks and How to Prevent Them

Hoang Vu,4 min read

March 25, 2026

CSRF (Cross-Site Request Forgery) tricks an authenticated user's browser into sending unintended state-changing requests to your server.

Unlike XSS, attackers do not need to execute script on your origin. They abuse the fact that browsers may automatically attach session cookies.

Loading CSRF remotion demos...

1. CSRF attack flow in practice

A typical chain:

  1. Victim is logged into a target app.
  2. Attacker hosts a malicious page or link.
  3. Victim visits attacker page while still authenticated.
  4. Browser sends request to target app with existing cookies.
  5. Server accepts request if no CSRF defense exists.

This makes malicious requests look like normal user actions from server perspective.

Deep dive: why browser behavior enables CSRF

The key issue is ambient authority: browsers automatically include cookies that represent authenticated state. If your server accepts those cookies without additional intent verification, attacker-controlled pages can trigger side effects.

In other words, authentication is present, but user intention is missing.

CSRF ATTACK FLOW

1. Victim authenticated
2. Session cookie active
3. Victim opens attacker page
4. Forged request sent
5. Server may accept action

2. Why CSRF is dangerous

  1. It leverages valid authenticated sessions.
  2. It often runs silently without user awareness.
  3. Logs may show legitimate user/session identifiers, making incident analysis harder.

Typical impact includes profile changes, unauthorized payments, permission changes, or account takeover workflows.

In production incidents, CSRF impact is often amplified by "silent" endpoints (no secondary confirmation, no unusual telemetry) where actions succeed with minimal friction.


3. Core defense: anti-CSRF tokens

For state-changing operations, require a server-validated token bound to session/user context.

High-level pattern:

  1. Server issues per-session or per-request token.
  2. Client includes token in form field or custom header.
  3. Server verifies token before processing mutation.
  4. Missing/invalid token returns 403.

Token properties:

  • unguessable,
  • short-lived when possible,
  • rotated or scoped appropriately for sensitive actions.

Implementation detail that matters

CSRF tokens should be validated on every mutation path, including internal admin forms and rarely used legacy endpoints. A single unprotected mutation can invalidate an otherwise strong security posture.

CSRF TOKEN VALIDATION

Server issues CSRF token
Client sends token in mutation request
Server validates token against session
Reject if missing / mismatch

4. SameSite cookies are a major protection layer

Use cookie attributes deliberately:

  1. SameSite=Lax is a strong default for many apps.
  2. SameSite=Strict is stronger but may impact UX flows.
  3. SameSite=None requires Secure and should be used only when cross-site needs are explicit.

Also always set:

  • Secure on HTTPS,
  • HttpOnly for session cookies.

SameSite reduces cross-site cookie sending, which directly limits CSRF surface.

Deep dive: choosing SameSite mode

  1. Lax: good baseline for many apps, allows top-level navigation scenarios.
  2. Strict: strongest isolation, but can break some cross-site entry flows.
  3. None: only for explicit cross-site requirements and always with Secure.

Do not treat SameSite as a complete CSRF replacement; treat it as a high-value layer in a broader design.

CSRF COOKIE + POLICY SHIELD

SameSite=Lax/Strict
Secure cookie
HttpOnly session
Origin/Referer check

5. HTTP method discipline matters

Do not use GET for state-changing actions.

Why:

  1. GET is easy to trigger via links or embedded resources.
  2. GET URLs are cacheable/shareable/history-visible.
  3. Idempotent semantics of GET should be preserved.

Use POST/PUT/PATCH/DELETE for mutations and enforce CSRF verification there.

This also improves system clarity: request semantics become predictable for developers, reviewers, observability tools, and incident response.


6. Additional server-side checks

Defense-in-depth options:

  1. verify Origin and/or Referer headers where appropriate,
  2. reject suspicious cross-site mutation patterns,
  3. implement strict CORS for APIs,
  4. add re-auth or step-up verification for high-risk actions.

No single control is enough for all architectures.

Risk-based hardening examples

For high-risk operations (payment method changes, email/2FA resets):

  1. require fresh re-auth or step-up verification,
  2. bind requests to anti-replay tokens,
  3. add anomaly detection for origin/user-agent shifts,
  4. log structured security events for forensic workflows.

7. Practical Next.js implementation pattern

For cookie-based auth apps:

  1. issue CSRF token at session/form bootstrap,
  2. send token in custom header for mutations,
  3. verify token in API route or server action,
  4. reject on mismatch and log security event,
  5. combine with SameSite cookie strategy.

If your API is token-based (Authorization header, no ambient cookies), CSRF risk is often lower, but you still need clear threat modeling.

If you support both browser cookie auth and API token auth in the same platform, document boundaries explicitly. Mixed-mode architectures are where CSRF assumptions often break.


8. Quick checklist

  1. Never mutate state through GET.
  2. Require CSRF token on every mutation endpoint.
  3. Configure SameSite, Secure, HttpOnly correctly.
  4. Validate Origin/Referer when possible.
  5. Separate low-risk and high-risk flows with stricter controls.
  6. Monitor unusual mutation spikes and failed CSRF validations.

9. Conclusion

CSRF is fundamentally a trust confusion problem: the server trusts cookies that the browser attaches automatically.

Robust mitigation comes from combining:

  • anti-CSRF token verification,
  • correct cookie attributes,
  • safe HTTP method usage,
  • layered server-side checks.

When these are in place, CSRF moves from a hidden operational risk to a controlled security boundary.

The practical goal is not "one anti-CSRF feature" but a design where each layer independently narrows attack success probability.

2026 © @hoag/blog.