Introduction: Understanding CSRF
Cross-Site Request Forgery (CSRF) is a type of security vulnerability where attackers trick authenticated users into unknowingly executing unwanted actions on websites they’re logged into. CSRF attacks exploit the trust that a website has in a user’s browser by forcing the victim’s browser to send forged HTTP requests to a target site.
Why CSRF Prevention Matters:
- Protects users from performing unintended actions
- Prevents unauthorized transactions, data modification, or account takeovers
- Required for compliance with security standards like OWASP Top 10
- Maintains trust in your application and brand reputation
Core CSRF Concepts
How CSRF Attacks Work
- User authenticates with a legitimate website (e.g., banking site)
- Authentication creates a session cookie stored in the user’s browser
- Without logging out, user visits a malicious website
- The malicious site contains code that submits a request to the legitimate site
- The browser automatically includes the session cookie with the request
- The legitimate site processes the request as authentic since it contains valid credentials
Key Principles of CSRF Prevention
Principle | Description |
---|---|
Defense in Depth | Implement multiple layers of protection rather than a single solution |
Verification of Intent | Ensure requests come from legitimate UI interactions |
Same-Origin Policy | Browsers restrict how documents/scripts from one origin interact with resources from another |
Proper Session Management | Sessions should expire appropriately and require re-authentication for sensitive actions |
CSRF Prevention Methods
1. Synchronizer (CSRF) Tokens
The most common and effective defense against CSRF attacks.
Implementation Steps:
- Generate a unique, unpredictable token for each user session
- Include this token as a hidden field in all forms
- Send the token in custom HTTP headers for AJAX requests
- Validate the token on the server for every state-changing request
<form action="/transfer" method="post">
<input type="hidden" name="csrf_token" value="random_token_value">
<!-- Form fields -->
<button type="submit">Submit</button>
</form>
// AJAX request with CSRF token
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify(data)
});
2. Same-Site Cookies
Implementation:
Set-Cookie: sessionid=abc123; SameSite=Strict; Secure; HttpOnly
SameSite Values:
Value | Behavior | Use Case |
---|---|---|
Strict | Cookies only sent in first-party context | Highest security; may impact legitimate cross-site functionality |
Lax | Cookies sent with GET requests in top-level navigations | Good balance of security and usability (default in modern browsers) |
None | Cookies sent in all contexts (requires Secure flag) | Use only when cross-site functionality is absolutely necessary |
3. Custom Request Headers
For AJAX requests, custom headers like X-Requested-With: XMLHttpRequest
provide protection because browsers don’t allow custom headers in cross-site requests without CORS.
4. Double Submit Cookie
- Set a CSRF token as a cookie
- Include the same token as a request parameter or header
- Verify that both values match on the server
5. Re-Authentication for Sensitive Actions
Require password re-entry or two-factor authentication for critical operations like:
- Password changes
- Email changes
- Financial transactions
- Account deletion
CSRF Prevention by Framework
Framework | Built-in Protection | Implementation |
---|---|---|
Spring | CSRF tokens | <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/> |
Django | CSRF middleware | {% csrf_token %} in forms |
Laravel | CSRF middleware | @csrf directive in forms |
Express.js | csurf middleware | <input type="hidden" name="_csrf" value="<%= csrfToken %>"> |
ASP.NET | AntiForgeryToken | @Html.AntiForgeryToken() in forms |
Ruby on Rails | CSRF protection | <%= csrf_meta_tags %> in layout |
Angular | HttpClientXsrfModule | Automatically handles CSRF tokens |
React | No built-in (use custom) | Manually include token from cookies/state |
Common CSRF Challenges and Solutions
Single Page Applications (SPAs)
Challenge: Traditional form-based CSRF protection doesn’t work well with API-based SPAs.
Solution:
- Use custom HTTP headers for all state-changing requests
- Implement token-based authentication (JWT) with proper storage
- Utilize CSRF tokens in request headers or in the Authorization header
Third-Party Integrations
Challenge: Third-party services may not support your CSRF protection.
Solution:
- Use separate, dedicated endpoints for third-party integrations
- Implement additional authorization checks for these endpoints
- Consider using signed requests with API keys instead of cookies
GET Requests for State Changes
Challenge: GET requests should never change state, but sometimes they do.
Solution:
- Always use POST/PUT/DELETE for state-changing operations
- Apply CSRF protection to all non-GET methods
- Refactor existing GET endpoints that change state
Testing for CSRF Vulnerabilities
Manual Testing Checklist
- [ ] Identify all state-changing functionality
- [ ] Check if CSRF tokens are implemented
- [ ] Verify token validation on the server
- [ ] Test token expiration and rotation
- [ ] Confirm tokens are unpredictable
- [ ] Verify proper SameSite cookie attributes
Automated Testing Tools
- OWASP ZAP
- Burp Suite Professional
- Acunetix
- Netsparker
- Custom scripts with tools like Selenium
CSRF Prevention Implementation Checklist
- [ ] Implement CSRF tokens for all forms and AJAX requests
- [ ] Set SameSite=Lax or Strict for all cookies
- [ ] Use HttpOnly and Secure flags for sensitive cookies
- [ ] Require re-authentication for sensitive operations
- [ ] Implement proper token validation on the server
- [ ] Apply CSRF protection to all state-changing operations
- [ ] Add referrer and origin validation as an additional layer
- [ ] Test CSRF protection with both automated tools and manual testing
- [ ] Document CSRF protection mechanisms for developers
Best Practices
- Never use GET requests for state-changing operations
- Generate cryptographically strong, random tokens
- Tie CSRF tokens to the user’s session
- Implement proper error handling for invalid or missing tokens
- Rotate tokens after successful form submissions
- Use HTTPS for all traffic to prevent token leakage
- Don’t store CSRF tokens in localStorage (vulnerable to XSS)
- Apply defense in depth with multiple protection mechanisms
Resources for Further Learning
Official Documentation
Security Tools
Books and Courses
- “Web Application Security: A Beginner’s Guide” by Bryan Sullivan
- “The Tangled Web: A Guide to Securing Modern Web Applications” by Michal Zalewski
- “Web Security Academy” by PortSwigger
- SANS SEC542: Web App Penetration Testing and Ethical Hacking