On this page
A browser automatically attaches eligible cookies to requests. That convenience creates a risk: another site may induce a visitor's browser to submit a request using an existing authenticated session. If the API accepts the mutation without checking user intent, the attacker can act through the victim's browser without knowing the session cookie.
Model which credentials are sent automatically
Cross-site request forgery targets requests that receive ambient credentials, especially cookies. Begin by listing every state-changing endpoint: account updates, billing changes, administrative actions, logout, and integration management. Include legacy form endpoints and alternate content types rather than checking only your newest JSON routes.
Use the API Latency Tester against a non-sensitive test endpoint to confirm statuses, but use an actual browser and controlled test pages for CSRF behavior. A standalone HTTP client does not reproduce cookie inclusion, SameSite rules, or browser navigation contexts.
The OWASP CSRF Prevention Cheat Sheet recommends established framework protections where available. Prefer those mechanisms to inventing a new token protocol, then verify how they interact with your API framework and frontend deployment.
Keep safe methods free of side effects
GET and HEAD should not change business state. Browsers, crawlers, preview tools, and prefetchers may invoke safe methods without a deliberate user action. A CSRF token added only to POST handlers cannot protect a destructive GET endpoint that remains reachable elsewhere.
Choose a consistent set of unsafe methods requiring protection, normally including POST, PUT, PATCH, and DELETE. Method alone does not prove risk, but it provides a reliable middleware boundary. Do not exempt a route simply because its UI is hidden or it usually receives AJAX requests.
Reject unexpected content types on JSON-only mutation endpoints. An attacker-controlled HTML form can send some content types without preflight; a permissive parser might interpret that input unexpectedly. Content-type restrictions are useful defense in depth, but should not be your sole CSRF control unless a carefully reviewed architecture explicitly supports that choice.
Separate machine-to-machine webhook routes from browser-session routes. A webhook normally uses its own signature authentication and does not rely on cookies. Exempting that specific route may be appropriate; exempting an entire API prefix because one webhook cannot supply a browser token is not.
Implement a proven token pattern
For a stateful application, a synchronizer token is a strong conventional choice. Generate a secret value associated with the authenticated session, expose it through a legitimate same-origin page or protected bootstrap response, and require it on mutations. Compare it using the framework's supported implementation.
const response = await fetch('/api/account/display-name', {
method: 'PATCH',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfTokenFromBootstrap
},
body: JSON.stringify({ displayName: 'Morgan' })
});
if (response.status === 403) {
showSessionOrSecurityError();
}This browser example assumes the server verifies both the authenticated session and the CSRF token before applying the change. Supplying a header without verification creates no protection. The token should not be placed in a query string, where URLs can spread through logs, browser history, and referrers.
For stateless designs, use a carefully implemented signed double-submit approach bound to the authenticated context, as recommended by established guidance. A naive equality check between an attacker-injectable cookie and a request field can be vulnerable to cookie injection. Do not confuse a CSRF token with an authentication credential; both checks are needed.
Add origin checks and deliberate cookie settings
Validate the Origin header for unsafe requests against an exact allowlist. Where your supported clients legitimately omit Origin, define a reviewed fallback, often involving Referer validation or an explicit non-browser authentication path. Do not silently treat every missing header as proof of trust.
Determine the target origin using trusted deployment configuration. Reverse proxy headers can help only when they are accepted exclusively from trusted proxies. Comparing against an attacker-controlled Host or forwarded header can undermine an otherwise sound check.
Set session cookies with Secure, HttpOnly, and an intentional SameSite policy. Strict may disrupt valid cross-site entry flows; Lax offers useful restrictions but is not a substitute for endpoint protections. Cross-site cookie use generally requires SameSite=None and Secure, bringing additional dependence on browser privacy policies.
Read MDN's Set-Cookie reference to understand these options. Same-site and same-origin are different concepts: related subdomains can be same-site while still representing distinct origins and different trust levels. A compromised sibling subdomain can therefore matter to your threat model.
Test real request paths and recovery behavior
Create controlled attacker pages attempting an ordinary form POST, a cross-origin fetch, and a navigation to any historical mutation URL. Confirm that rejected requests do not alter state, even when the attacker cannot read the response. Testing only whether JavaScript receives JSON misses the actual CSRF threat.
Check session lifecycle events. Tokens may change when a user logs in, logs out, switches accounts, or rotates a session. Decide how multiple tabs recover from a stale token without creating an infinite retry loop. A fresh bootstrap request may be appropriate, but never automatically repeat a potentially completed mutation without understanding its outcome.
CORS should not grant arbitrary origins credentialed read access to a token bootstrap endpoint. It is a separate control with separate semantics; the Fetch standard describes how browser credentials and origin policy interact. A correctly configured CSRF layer should fail closed when its token is missing or invalid.
Use the JSON Formatter to define a safe, consistent rejection body that contains no expected token value. Record deployment assumptions and route exemptions in your Backend & APIs documentation. Log bounded rejection reasons for diagnosis, but do not record CSRF tokens or whole session cookies. XSS can defeat many browser-side protections, so CSRF controls complement rather than replace script-injection defenses.
Work through an account-change test
Create two test accounts and sign into one in a browser profile dedicated to security testing. Record its current notification address. From a different origin that you control, submit a form targeting the address-change endpoint with a replacement address. The expected result is rejection and an unchanged account record, not merely an unreadable browser response. Repeat with a missing token, a token from the other account, and a valid token paired with a forbidden origin.
Include login CSRF in this exercise. An attacker may try to sign a victim into the attacker's account, causing later activity or sensitive uploads to land there. Review the framework's pre-authentication token handling and session rotation rather than assuming that unauthenticated endpoints never need protection. Logout deserves an explicit decision too: forced logout can disrupt work even though it does not directly disclose data.
Finally, inspect monitoring after these tests. A rejection event should identify the route, policy reason, and correlation identifier without capturing submitted secrets. Check that the same request cannot reach business logic through a trailing-slash alias or older API version. This turns a middleware unit test into evidence that the deployed routing boundary actually enforces the policy.
Key takeaways
- Identify every cookie-authenticated mutation, including old forms and alternate routes.
- Prefer framework-backed synchronizer tokens or a properly signed double-submit design.
- Combine token checks with exact origin validation and intentional cookie attributes.
- Test browser behavior, session transitions, and rejected-request side effects.
Reach designers & developers at work
Put your product next to the tools people use every day. One sponsor per placement, no tracking pixels.
Become a sponsor