What is CSRF and why you need protection
Cross-Site Request Forgery (CSRF) is an attack where a malicious site tricks the user's browser into executing unauthorized actions on a site where they're authenticated. For example: you open an email with an image that's actually a hidden form transferring money from your bank.
The attack works because browsers automatically send cookies with every request to the corresponding domain. If you're logged into your bank and visit a malicious site, that site can trigger requests that your browser will authenticate with your session cookies.
CSRF tokens solve this: they're unique, unpredictable values generated per session and validated on the server. The attacker can't know the token because it's stored only in your legitimate session, not in the cookies the browser sends automatically.
How to implement CSRF tokens correctly
Basic implementation has three steps:
1. Generation: When creating the user session, generate a cryptographically secure token (minimum 128 bits, ideally 256). Store it in server session, NOT in cookies.
2. Inclusion: In each HTML form, include the token as a hidden field:<input type='hidden' name='csrf_token' value='your-token-here'>
For AJAX requests, send it in custom headers: X-CSRF-Token.
3. Validation: On every POST/PUT/DELETE, the server compares the received token with the one stored in session. If they don't match or it's missing, reject the request with 403 error.
Common mistakes: Using the same token for all users (defeats the purpose), storing tokens in localStorage (accessible via XSS), or not validating on critical read-only actions (GET that modifies state is an anti-pattern).
Tokens per session vs tokens per request
There are two main approaches for token validity:
Token per session: A single token valid for the user's entire session. Pros: simple to implement, doesn't affect usability (page refresh doesn't break forms). Cons: if the token leaks, it's valid until session expires.
Token per request: Each form/request generates a new token that's invalidated after use. Pros: maximum security, minimal attack window. Cons: complexity in apps with multiple tabs, issues with browser 'back' button.
For most apps, token per session is sufficient if you combine with:
- 256-bit tokens (impossible to brute-force guess)
- Regeneration on privilege changes (login, upgrade to admin)
- Strict validation without exceptions
- Mandatory HTTPS (prevents man-in-the-middle)
Tokens per request are for online banking, critical admin panels or demanding regulatory compliance.
Framework integration and special cases
Most modern frameworks have built-in CSRF protection, but you need to configure it correctly:
Django: {% csrf_token %} in templates. CsrfViewMiddleware middleware enabled by default. For AJAX: read csrftoken cookie and send in X-CSRFToken header.
Rails: <%= csrf_meta_tags %> in layout. protect_from_forgery with: :exception in ApplicationController. Automatic verification in forms generated with helpers.
Laravel: @csrf in Blade templates. VerifyCsrfToken middleware. For API routes, use Sanctum with SPA authentication mode.
Special cases:
- Stateless REST APIs: If you don't use sessions, CSRF doesn't apply (use OAuth2/JWT). But if your API shares cookies with frontend, you DO need protection.
- File uploads: Include token in multipart form, not as query string parameter.
- Webhooks: Don't use CSRF tokens (external caller has no session). Implement HMAC signatures or shared secrets.