CSRF (Cross-Site Request Forgery) Deep Dive
CSRF happens when a website accidentally lets an attacker trigger actions as you because your browser automatically includes your login cookies on requests to that site.
Practical note: CSRF usually ships when teams rely on âsame-site cookiesâ as a magic shield â then one exception (or legacy browser/client) breaks the assumption.
Deep reason CSRF exists (why the browser behaves this way)
The web was designed so browsers can automatically maintain sessions using cookies. When youâre logged into bank.example, your browser will attach session cookies to requests to bank.exampleâeven if those requests were initiated from another site (a link, a form, an image, or a script-driven request).
- Cookies are ambient authority: they âride alongâ with requests to a domain.
- Browsers donât know user intent: âdid the user click a real button or was it triggered elsewhere?â
- Same-origin policy protects reading, not sending: SOP blocks cross-site reading of responses, but historically did not block sending requests.
First principles mental model
Think of CSRF as a three-part mismatch:
- Authentication: the server trusts the session cookie.
- Authorization: the user is allowed to perform the action.
- Intent verification: the server fails to verify that the request came from the siteâs own UI and was deliberate.
CSRF happens when you have cookie-based auth and a state-changing endpoint, and the server does not demand an additional unforgeable signal of intent (like a CSRF token).
When CSRF is in-scope (and when it isnât)
Typically at risk
- Session cookies used for auth (classic web apps, some SPAs with cookie sessions)
- State-changing requests: profile updates, password/email change, transfer, add payee, change address
- Endpoints that accept âsimple requestsâ (e.g., form-like POSTs) and do not require special headers
Often not CSRF (or lower risk)
- Pure token-based APIs where credentials are in an
Authorizationheader and never auto-sent by the browser - Read-only GET endpoints (CSRF targets state change; GET should be safe/idempotent)
- Apps that already enforce SameSite appropriately and use CSRF tokens for unsafe methods
Vulnerable vs secure code patterns (Node.js)
Vulnerable pattern (cookie session + no CSRF protection)
// Node/Express (example)
// Cookie-based session (e.g., express-session) means browser auto-sends cookies
app.post("/account/email", async (req, res) => {
// â Missing CSRF protection: server only checks "is logged in"
if (!req.user) return res.status(401).json({ error: "Not authenticated" });
const newEmail = String(req.body.email || "");
await users.updateEmail(req.user.id, newEmail);
res.json({ ok: true });
}); Secure pattern (CSRF token + SameSite)
// â
Defensive pattern using csurf (token-based CSRF protection)
// Also ensure cookies are configured with SameSite and Secure.
import express from "express";
import cookieParser from "cookie-parser";
import csrf from "csurf";
const app = express();
app.use(express.urlencoded({ extended: false }));
app.use(express.json());
app.use(cookieParser());
// Example cookie session cookie settings (conceptual)
// res.cookie("sid", value, { httpOnly: true, secure: true, sameSite: "lax" });
const csrfProtection = csrf({
cookie: {
key: "__Host-csrf",
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/"
}
});
// Serve token for forms/SPA
app.get("/csrf-token", csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// Protect state-changing routes
app.post("/account/email", csrfProtection, async (req, res) => {
if (!req.user) return res.status(401).json({ error: "Not authenticated" });
const newEmail = String(req.body.email || "");
await users.updateEmail(req.user.id, newEmail);
res.json({ ok: true });
}); Modern mitigations and how they fit together
1) SameSite cookies (baseline defense)
- Lax: good default for many apps; reduces cross-site cookie sending for unsafe contexts while keeping usability.
- Strict: strongest, but can break legitimate cross-site navigation flows (e.g., deep links, federated login journeys).
- None: allows cross-site cookies but requires Secure (HTTPS). Use only when you truly need third-party cookie behavior.
2) CSRF tokens (primary enforcement for unsafe methods)
- Token must be unpredictable, tied to the user/session, and required on state-changing requests.
- Works for classic forms and SPAs (fetch token from same origin, send in body or header).
- Prefer validating for POST/PUT/PATCH/DELETE and any action that changes state.
3) Origin/Referer checks (defense-in-depth)
- Validate
Originheader for unsafe methods where available. - Use
Refereras a fallback, but handle missing cases safely. - Best used alongside tokens; do not rely on it alone for all flows.
4) Re-authentication / step-up verification (for high-risk actions)
- Password re-entry, OTP, WebAuthn, or transaction confirmation for actions like changing email/password, adding payee, transfers.
Types / variants (and why they differ)
1) Classic form-based CSRF
Works when a server accepts form-like requests and relies only on cookies for auth. The key issue is missing intent verification.
2) CSRF against JSON endpoints
Many JSON endpoints are safer because browsers impose restrictions on cross-site requests that use non-simple headers/content-types. But risk returns if the endpoint accepts âsimpleâ encodings or if the app exposes a CSRF token improperly.
3) Login CSRF
The victim is tricked into being logged into the attackerâs account on the target site. Impact is usually session confusion (victim performs actions under attackerâs session) rather than direct money movement.
4) State-changing GET (a design bug that enables CSRF)
If a GET request changes state (e.g., â/delete?id=...â), then many browser behaviors make it easier to trigger accidentally. The fix is: GET must be idempotent; use POST with CSRF defenses for changes.
Detection workflow (experienced-style, systematic)
This avoids âtry random stuffâ and instead confirms the exact preconditions for CSRF.
Step A â Identify state-changing endpoints
- Account settings changes, payments, address changes, notifications, security settings
- Admin actions: approve/refund/export/role changes
- Hidden actions: âtoggleâ, âenableâ, âlinkâ, âdisconnectâ, âresendâ
Step B â Confirm the auth mechanism
- Does the app authenticate via cookies?
- Are there anti-CSRF mechanisms already (token fields, required headers, origin checks)?
- How are cookies configured (SameSite, Secure, HttpOnly)?
Step C â Look for missing intent verification
- Unsafe method endpoint accepts requests without a CSRF token.
- Token exists but is not validated correctly (accepted when missing/empty, not bound to session, reused across users).
- Endpoint accepts alternate content-types that bypass the intended protection.
Safe validation workflow (defensive verification)
- Baseline: capture a legitimate request that changes state (e.g., update a low-risk preference).
- Check defenses: identify whether a CSRF token is present/required and whether the app enforces SameSite.
- Negative test: confirm that the server rejects unsafe requests when the token is missing/invalid (expect
403or equivalent). - Cross-origin reasoning: verify the server requires a same-site signal (token and/or origin check) for unsafe methods.
- Document evidence: request/response showing missing enforcement, plus the observed state change (minimal and reversible).
Exploitation progression (attacker mindset)
This explains how real attackers think about CSRF without giving copy/paste steps. The goal is to understand the decision process.
Phase 1: Find actions that matter
- Account changes: email/password, 2FA settings, recovery options
- Financial/business actions: approve, transfer, purchase, change payout
- Privilege actions: role changes, access grants, API key creation
Phase 2: Check whether cookies are the auth mechanism
- If auth rides on cookies, the browser may send credentials automatically.
- If auth requires an explicit header token, CSRF risk drops (unless token is also ambient via cookie).
Phase 3: Look for missing intent verification
- Endpoints that do not require a CSRF token for unsafe methods.
- Endpoints with inconsistent CSRF enforcement (some routes protected, siblings not).
- Endpoints that accept alternate request formats that bypass protection.
Phase 4: Increase impact via chaining
- Prefer actions that persist (change email, add device, create API key).
- Look for workflows where one action enables a bigger one (e.g., add payee â transfer later).
Tricky edge cases & bypass patterns (what attackers look for)
- Inconsistent CSRF coverage: new endpoints added without CSRF middleware, or admin endpoints using different router stacks.
- Token not bound correctly: token accepted across sessions/users, or validated only for presence not correctness.
- Multiple content-types: route protects JSON but still accepts form-urlencoded or querystring variants.
- State-changing GET: âtoggle/enable/deleteâ via GET increases accidental triggering and makes defenses harder.
- Login CSRF/session confusion: user ends up in attackerâs session and performs actions unknowingly.
- Cross-site flows: OAuth and SSO journeys can force SameSite=None; these flows need careful CSRF protection at the app layer.
- Subdomain/tenant complexity: cookies scoped too broadly (e.g.,
.example.com) can enlarge the CSRF blast radius between apps. - Legacy browser behavior: older clients and embedded browsers may not enforce SameSite consistently.
Confidence levels (how sure are you?)
| Confidence | What you observed | What you can claim |
|---|---|---|
| Low | Cookie-authenticated state change exists, but defenses are unclear or inconsistent across environments | âPotential CSRF exposure; needs verification of token enforcement and SameSite behaviorâ |
| Medium | State-changing endpoint lacks token enforcement or accepts unsafe requests in some paths | âLikely CSRF due to missing intent verification on a cookie-authenticated endpointâ |
| High | Repeatable evidence: missing/invalid token accepted (or no token at all) plus confirmed state change (minimal/reversible) | âConfirmed CSRF risk with demonstrated state change under ambient authenticationâ |
Fixes that actually hold in production
1) Make cookies safer
- Set SameSite=Lax by default; use Strict where compatible.
- Use Secure for HTTPS-only cookies, and HttpOnly to reduce script access to session cookies.
- Prefer __Host- cookie prefix for sensitive cookies (no Domain attribute, path=/, Secure) when feasible.
2) Enforce CSRF tokens for unsafe methods
- Require token for
POST/PUT/PATCH/DELETEand any state-changing action. - Bind token to session/user context and rotate appropriately.
- For SPAs: fetch token from same origin and send via header (e.g.,
X-CSRF-Token).
3) Add origin checks (defense-in-depth)
- Validate
Originon unsafe methods; fallback toRefererif needed. - Fail closed for sensitive actions if origin cannot be determined (with careful exception handling for known safe cases).
4) Step-up for critical actions
- Re-authentication / OTP for high-risk endpoints to reduce CSRF and stolen-session impact.
Regression prevention (how to prevent regressions)
- Framework default: enable CSRF middleware by default and require explicit opt-out (with justification).
- Tests: integration tests ensuring unsafe endpoints reject missing/invalid CSRF tokens (expect
403). - Code review rule: any cookie-authenticated state-changing endpoint must show CSRF enforcement.
- Monitoring: log CSRF validation failures and watch for spikes (could indicate probing or broken clients).
- Design rule: GET must not change state; enforce via linting or routing conventions.
Interview-ready answers (60-second + 2-minute)
60-second answer
CSRF is when a userâs browser performs a state-changing action on a site using the userâs existing session cookies, without a reliable same-site proof of intent. It happens because cookies are sent automatically. The fix is to require intent verification on unsafe methodsâCSRF tokensâand to harden cookies with SameSite and Secure. For high-risk actions, add re-auth or step-up verification. I validate it by checking cookie-based auth plus missing token/origin enforcement, using reversible low-risk actions and clear before/after evidence.
2-minute answer
I model CSRF as an âambient authorityâ problem: cookies are automatically attached to requests, so the server must verify intent, not just authentication. I start by enumerating state-changing endpoints and confirming the auth mechanism. If the endpoint relies on cookies and doesnât require a CSRF token (or validates it incorrectly), itâs vulnerable. SameSite helps a lot, but itâs not a universal substitute because real apps often need cross-site flows (SSO/OAuth), and legacy clients can behave differently. So production-grade defense is SameSite as baseline plus CSRF tokens for unsafe methods, optionally origin checks as defense-in-depth, and step-up verification for critical actions. Then I prevent regressions with tests that assert missing/invalid tokens are rejected and with a code review rule that unsafe cookie-auth endpoints must show CSRF enforcement.
Checklist (quick review)
- Does the app authenticate via cookies for browser requests?
- Are all state-changing endpoints protected (token/origin checks) consistently?
- Is SameSite configured appropriately (and not overridden to None unnecessarily)?
- Are there any state-changing GET endpoints?
- Do endpoints accept alternate content-types or routes that bypass CSRF middleware?
- Are high-risk actions protected with step-up verification (re-auth/OTP/WebAuthn)?
- Are there automated tests for CSRF enforcement on unsafe methods?
Remediation playbook
- Contain: protect the highest-risk endpoints first (email/password change, payouts, approvals).
- Fix baseline: set session cookies to Secure + HttpOnly + appropriate SameSite (Lax/Strict where possible).
- Implement tokens: add CSRF middleware and enforce on all unsafe methods by default.
- Harden flows: add origin checks and step-up verification for critical actions.
- Scope: search for endpoints outside the main router stack (admin, legacy, internal tools) that may skip middleware.
- Test: add integration tests asserting missing/invalid token returns
403. - Monitor: alert on CSRF validation error spikes; review blocked attempts and broken clients.
Interview Questions & Answers (Easy â Hard)
Easy
- What is CSRF?
A: Plain: Itâs when your browser performs an action on a site as you, without your intended consent. Deep: It happens because cookies are sent automatically, so the server must verify intent with CSRF tokens and safer cookie settings. - Why do cookies matter for CSRF?
A: Plain: Cookies âride alongâ automatically. Deep: That makes them ambient authorityârequests can look authenticated even if initiated from another site. - Is CSRF possible if an API uses an Authorization header token?
A: Plain: Usually no, because the browser wonât auto-send that header cross-site. Deep: Risk returns if the token is stored in a cookie or if the app relies on cookies for any state-changing actions. - What is the best primary defense?
A: Plain: CSRF tokens on state-changing requests. Deep: Tokens provide a same-site proof of intent; combine with SameSite cookies for strong coverage. - What does SameSite do?
A: Plain: It limits when cookies are sent in cross-site requests. Deep: Lax/Strict reduce cross-site cookie sending, lowering CSRF risk, but real apps sometimes need exceptions (SSO/OAuth flows). - Why is âGET should not change stateâ relevant to CSRF?
A: Plain: GET is easy to trigger. Deep: If GET changes state, many normal browser behaviors can cause unintended actions; use POST with CSRF protection for changes. - Follow-up: Is a hidden form field enough?
A: Plain: Not unless itâs a real CSRF token. Deep: The value must be unpredictable and server-validated, bound to the session/user context.
Medium
- How do you validate CSRF in an interview-safe way?
A: Plain: I check if state changes rely on cookies and whether tokens/origin checks are enforced. Deep: I capture a baseline request, confirm defenses, and ensure missing/invalid tokens are rejectedâusing low-risk reversible actions and clear before/after evidence. - Scenario: A site has SameSite=Lax. Is CSRF solved?
A: Plain: Itâs reduced, not guaranteed solved. Deep: Cross-site flows and exceptions can re-open risk; tokens provide consistent intent verification across endpoints and clients. - Scenario: A JSON endpoint rejects cross-origin requests. Can it still be CSRF?
A: Plain: Possibly, depending on what it accepts. Deep: If it accepts simple request formats or relies on cookies without token enforcement, you can still get CSRF-like state changes. Evaluate the server-side enforcement, not only the browser behavior. - Whatâs âlogin CSRFâ?
A: Plain: Victim ends up logged into the attackerâs account on the site. Deep: Impact is session confusionâvictim actions attach to attackerâs account; mitigations include anti-CSRF on login, session rotation, and confirmation steps. - Follow-up: If you implement tokens, where should they live for a SPA?
A: Plain: Fetch from same origin and send back in a header. Deep: Keep tokens tied to session and avoid exposing long-lived tokens; store the session cookie HttpOnly; handle token rotation and caching carefully. - Follow-up: Why add origin checks if you already have tokens?
A: Plain: Defense-in-depth. Deep: Origin checks can catch misconfigurations or endpoints missing middleware; they add an extra same-site signal for unsafe methods. - Scenario: A sensitive endpoint uses POST but still has no CSRF token. Whatâs your priority fix?
A: Plain: Add CSRF enforcement for unsafe methods. Deep: Enable middleware by default, ensure SameSite is set appropriately, and add step-up verification if the action is high risk.
Hard
- Scenario: The app uses OAuth/SSO and needs SameSite=None. How do you stay safe?
A: Plain: Rely more on CSRF tokens and strict flow validation. Deep: Use state parameters correctly for OAuth flows, enforce CSRF tokens on app actions, validate Origin on unsafe requests, and apply step-up verification for critical operations. - Scenario: Some routes are protected by CSRF middleware, but admin routes arenât. Why does this happen?
A: Plain: Different routers/stacks. Deep: Teams often mount admin/legacy routers without shared middleware. Fix by enforcing CSRF at the framework level by default and requiring explicit opt-out with review. - Scenario: You see a token in requests, but CSRF still works. What are the likely root causes?
A: Plain: Token isnât validated properly. Deep: It may not be bound to the session, may be accepted when missing/empty, may be reused across users, or may be bypassed via an alternate route/content-type that skips validation. - Scenario: A âchange emailâ endpoint is protected by tokens, but A tester can still cause damage. How?
A: Plain: CSRF isnât the only risk. Deep: If sessions can be stolen (XSS), or if thereâs no step-up verification, high-risk actions remain sensitive. Add re-auth/OTP and monitor unusual changes. - Scenario: Multi-subdomain apps share cookies at.example.com. What changes for CSRF?
A: Plain: Bigger blast radius. Deep: One sub-app can influence another if not isolated. Prefer host-only cookies where possible, segment apps, and treat cross-subdomain requests with the same intent controls. - Scenario: You must support legacy clients that donât handle SameSite consistently. Whatâs your strategy?
A: Plain: Rely on server-side CSRF enforcement. Deep: Tokens and origin checks provide consistent intent verification independent of client quirks; keep SameSite as baseline but donât assume itâs universal. - Follow-up: How do you prevent regressions at scale?
A: Plain: Make CSRF protection the default and test it. Deep: Global middleware, integration tests asserting403on missing/invalid tokens, and a code review rule for any cookie-auth unsafe endpoint. - Follow-up: How do you explain CSRF impact to a business stakeholder?
A: Plain: âA user can be tricked into performing actions they didnât intend.â Deep: Tie impact to concrete outcomes: account changes, unauthorized approvals, fraud workflows; then present layered mitigations and low-friction step-up for critical operations. - Follow-up: What trade-offs exist between SameSite=Strict and user experience?
A: Plain: Strict can break legitimate navigation. Deep: Cross-site entry points (deep links, SSO returns) may fail; many apps choose Lax + tokens, reserving Strict for the most sensitive surfaces.