CSRF Protection Checklist for REST APIs and Web Forms
A practical CSRF protection checklist covering token patterns, SameSite cookies, fetch metadata, and common implementation mistakes for APIs and forms.
Cross-site request forgery (CSRF) is one of the most misunderstood vulnerabilities in web security. Developers frequently implement partial defenses — checking the referrer header, switching to POST-only requests, or adding CORS headers — and conclude they are protected. These measures reduce noise but do not constitute real CSRF protection. A determined attacker bypasses all of them.
This checklist covers what actually works, where each defense applies, and the implementation details that determine whether your protection is genuine or cosmetic.
CSRF Protection Checklist for REST APIs and Web Forms
1. Understand when your application is actually vulnerable
CSRF is only exploitable when your application uses cookie-based authentication and the browser automatically sends those cookies on cross-origin requests. If your API authenticates via a custom Authorization header with a Bearer token stored in JavaScript memory (not a cookie), CSRF does not apply — the browser will not automatically attach that header to cross-origin requests.
If you use cookies for session management or authentication, work through the rest of this checklist.
2. Set SameSite on all authentication cookies
The SameSite cookie attribute is now the most practical first line of defense:
SameSite=Strict: The cookie is never sent on any cross-site request. This is the most restrictive and breaks use cases like navigating to your site from an email link while expecting the user to be authenticated.SameSite=Lax: The cookie is sent on top-level GET navigations but not on cross-site POST, PUT, DELETE, or fetch requests. This is the browser default for cookies without an explicitSameSiteattribute in modern browsers.SameSite=None; Secure: The cookie is sent on all cross-site requests. Only use this if your architecture requires cross-site cookie sharing, and always pair it with CSRF tokens.
For most applications, SameSite=Lax with Secure and HttpOnly flags is the right default. For highly sensitive operations, Strict is preferable.
3. Add synchronizer tokens for state-mutating operations
The synchronizer token pattern remains the most reliable CSRF defense and is required when SameSite=None is necessary or when you need defense-in-depth beyond cookie attributes.
Implementation requirements:
- Generate a cryptographically random token per session (or per form)
- Store it server-side, associated with the session
- Embed it in forms as a hidden field, or send it to the client for inclusion in a custom request header
- Validate the token on every state-mutating request before processing it
- Treat a missing or mismatched token as a rejected request, not a warning
Do not store the CSRF token in a location accessible to JavaScript if you have XSS vulnerabilities — XSS defeats CSRF tokens.
4. Use the Double Submit Cookie pattern for stateless APIs
If your API is stateless and you do not want to store tokens server-side, the Double Submit Cookie pattern is an alternative:
- Set a random value as a cookie and require the client to also send it in a custom request header or body parameter
- The server validates that both values match
An attacker cannot read the cookie value from a cross-origin context (assuming the cookie does not have SameSite=None), so the values cannot match in a forged request. This breaks down if the application has subdomain injection vulnerabilities, so it requires that all subdomains are trustworthy and that cookies are scoped appropriately with the domain attribute.
5. Validate the Origin and Referer headers as a secondary check
Check the Origin header on all state-mutating requests. If it does not match your expected origin(s), reject the request. Fall back to the Referer header when Origin is absent.
Important caveats:
- Do not rely on this as your only CSRF defense — headers can be absent in some privacy configurations
- Do not allowlist
nullas a valid origin —nullappears on requests from sandboxed iframes and file:// contexts and is commonly used in CSRF exploits - Maintain an explicit allowlist of permitted origins; do not infer from the request
6. Implement Fetch Metadata request headers validation
Modern browsers send Sec-Fetch-Site, Sec-Fetch-Mode, and Sec-Fetch-Dest headers that identify the context of a request. A request with Sec-Fetch-Site: cross-site combined with Sec-Fetch-Mode: navigate and a non-safe HTTP method is an unusual combination that warrants rejection.
Fetch Metadata policies are a defense-in-depth measure. They are not supported in all browser versions and should supplement, not replace, token-based defenses.
7. Do not use GET for state-mutating operations
GET requests should be idempotent and free of side effects. If a GET request can change state — delete a record, send an email, transfer funds — an attacker can trigger it with a simple image tag embedding the URL. This is not primarily a CSRF defense; it is a fundamental HTTP design principle with direct security implications.
8. Verify your framework's built-in CSRF protection is enabled
Most mature web frameworks (Django, Rails, Laravel, Spring Security, ASP.NET Core) include CSRF protection that is either on by default or trivial to enable. Check that:
- It has not been disabled for convenience
- API routes use appropriate exemptions only for truly stateless, token-authenticated endpoints
- You are not accidentally exempting too broad a set of routes
9. Review cookie scope settings
A cookie scoped too broadly creates vectors that token-based defenses cannot close:
- Set the
domainattribute to your specific domain, not a parent domain shared with other applications - Set the
pathattribute to the minimum necessary path - Use
Secureto prevent transmission over HTTP - Use
HttpOnlyto prevent JavaScript access when XSS protection is the goal
10. Test your CSRF protection before going to production
Manual verification: attempt a cross-origin form POST from a locally hosted HTML file to your application's state-mutating endpoints. If the request succeeds without a valid CSRF token, the protection is incomplete.
Automated testing: include CSRF in your DAST scan configuration. Tools like OWASP ZAP can probe for missing anti-CSRF tokens in form submissions.
Common Mistakes That Invalidate CSRF Protection
- Accepting CSRF tokens in query parameters rather than body or headers (query parameters appear in server logs and referrer headers)
- Generating tokens that are not truly random (sequential or predictable tokens are guessable)
- Failing to invalidate CSRF tokens on logout
- Allowing cross-origin requests with
Access-Control-Allow-Origin: *when cookies are in use
For teams building APIs and web applications that need security architecture reviewed or built from scratch, Start a build.