CWE-352: Cross-Site Request Forgery (CSRF)
Overview
Cross-Site Request Forgery (CSRF) is an attack that forces authenticated users to perform unwanted actions on a web application. Attackers craft malicious requests that appear to come from legitimate users, exploiting the user's authenticated session. Unlike XSS, which exploits a user's trust in a website, CSRF exploits a website's trust in the user's browser.
OWASP Classification
A01:2025 - Broken Access Control
Risk
CSRF vulnerabilities enable attackers to perform unauthorized actions on behalf of authenticated users:
- Account takeover: Change email, password, or security settings
- Financial fraud: Transfer funds, make purchases, change payment methods
- Data manipulation: Create, modify, or delete user data
- Privilege escalation: Add administrative users, change permissions
- System compromise: Execute administrative functions with user credentials
The impact is directly tied to the victim's privileges - CSRF against an administrator can compromise the entire application.
Remediation Steps
Core principle: Never allow authenticated, state-changing requests to be processed unless their origin and authenticity are verified using server-controlled mechanisms.
Identify state-changing endpoints lacking CSRF protection
- Review the security findings to identify the specific file, line number, and endpoint
- Identify all state-changing operations (POST, PUT, DELETE, or any action that modifies data)
- Check which endpoints rely solely on session cookies for authentication
- Locate forms and AJAX requests that don't include CSRF tokens
- Review framework configuration to determine if CSRF protection is disabled
- Map all endpoints that need CSRF protection
Use Synchronizer Token Pattern (Primary Defense)
- Generate cryptographically random CSRF tokens: Minimum 32 bytes for each user session
- Store token server-side: Save in user's session
- Include token in all forms: Add as hidden field
<input type="hidden" name="csrf_token" value="..."> - For AJAX requests, include token in custom headers:
X-CSRF-Token: ... - Validate token on server: Check for every state-changing request
- Reject missing/invalid tokens: Return 403 Forbidden for requests without valid token
- Use framework-provided CSRF protection: Django, Rails, Spring Security, Express csurf
- Why this works: Attackers cannot read the token due to same-origin policy, so forged requests will fail validation
Add SameSite cookie attribute (Defense in Depth)
- Set
SameSite=StrictorSameSite=Lax: On all session cookies - Use
SameSite=Strictfor maximum protection: Cookie sent only for same-site requests - Use
SameSite=Laxif navigation needed: Allows top-level navigation from external sites - Always combine with
SecureandHttpOnlyflags: HTTPS only, prevent JavaScript access - Example:
Set-Cookie: sessionid=...; SameSite=Strict; Secure; HttpOnly - Note: SameSite provides defense-in-depth but should not be the only protection
Apply Origin and Referer header validation
- Validate
Originheader: Must match your application's domain for all state-changing requests - If
Originabsent, checkRefererheader: Use as fallback - Reject unexpected origins or missing headers: Strict mode for sensitive operations
- Use allowlist of permitted origins: Your domains only
- Ensure validation over HTTPS: Prevent header tampering
- Implement as additional defense layer: Not as primary protection
Add re-authentication for critical operations
- For sensitive actions (password changes, fund transfers, account deletion), require password re-entry
- Implement step-up authentication for high-risk operations
- Use transaction confirmation mechanisms (email confirmation, 2FA)
- Add delays or rate limiting for sensitive operations
- Log all critical actions with full context (IP, user agent, timestamp)
Test and verify CSRF protection thoroughly
- Test that requests without CSRF tokens are rejected
- Test that requests with invalid tokens are rejected
- Test that tokens from different sessions are rejected
- Create cross-site test page that attempts forged POST request (should fail)
- Test GET requests cannot perform state changes (should be rejected)
- Verify SameSite cookies block cross-site requests in browser
- Test Origin/Referer header validation blocks external requests
- Ensure AJAX requests include CSRF tokens in headers
- Test framework CSRF protection is enabled and functioning
- Verify legitimate forms and AJAX requests still work
- Re-scan with the security scanner to confirm the issue is resolved
- Check for any new findings introduced by the changes
Testing and Verification
Manual CSRF Testing
<!-- Create a test page on attacker.com -->
<form action="https://vulnerable-app.com/transfer" method="POST">
<input type="hidden" name="to_account" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit();</script>
Expected result: Request should be rejected due to:
- Missing CSRF token
- SameSite cookie blocking
- Origin/Referer validation failure
Test Token Validation
- Submit request without token → Should fail
- Submit request with invalid token → Should fail
- Reuse token from different session → Should fail
- Submit GET request for state change → Should fail
Test Cookie Configuration
Verify session cookies have proper attributes:
Test Framework Protection
- Verify framework CSRF protection is enabled
- Test that protection applies to all state-changing endpoints
- Ensure AJAX requests include CSRF tokens
- Confirm error handling doesn't leak information
Migration Considerations
IMPORTANT: Adding CSRF protection may break existing integrations and AJAX requests.
What Breaks
- AJAX requests fail: JavaScript requests without CSRF tokens will be rejected
- API integrations broken: Third-party integrations or mobile apps calling your endpoints
- Legacy forms fail: Old HTML forms without CSRF tokens stop working
- Automated scripts break: Scripts using curl/wget need to include tokens
- Testing tools fail: Postman, automated tests need updated headers
Migration Approach
Gradual Rollout (Recommended)
-
Phase 1: Log CSRF violations without blocking
- Configure application to log failed CSRF validations
- Don't reject requests yet, just record violations
- Monitor logs to identify affected endpoints
-
Phase 2: Add tokens to all forms and AJAX requests
- Update all HTML forms to include CSRF tokens
- Configure AJAX libraries to include tokens in headers
- Test all functionality works with tokens
-
Phase 3: Enable enforcement (block invalid tokens)
- Configure application to reject requests without valid tokens
- Monitor error rates and user reports
- Be prepared to quickly add exemptions if needed
-
Phase 4: Monitor and adjust allowlist if needed
- Review logs for legitimate failures
- Add API endpoint exemptions as needed
- Remove temporary exemptions once migration complete
API Exemptions (Use Sparingly)
For legitimate API endpoints that can't use CSRF tokens:
- Only exempt endpoints using alternative authentication (API keys, OAuth)
- Never exempt endpoints relying solely on session cookies
- Document all exemptions and their justification
- Regularly review exemption list
Rollback Procedures
If CSRF protection breaks functionality:
- Disable enforcement temporarily: Use feature flag to revert to logging mode
- Add specific exemptions: Allowlist problematic endpoints temporarily
- Extend grace period: Keep logging-only mode longer to identify all issues
Testing Recommendations
- Test all forms include CSRF tokens
- Test AJAX requests include tokens in headers
- Test API clients can authenticate without CSRF tokens
- Test mobile app integration
- Verify allowlist/exemptions work correctly
- Load test: CSRF validation performance impact
Language-Specific Guidance
For detailed implementation guidance in specific programming languages and frameworks:
- Python (Django, Flask, FastAPI) - Django CSRF middleware, Flask-WTF, FastAPI CSRF protection, double submit cookie patterns
- Java (Spring, Servlets, JAX-RS) - Spring Security CSRF, manual servlet filters, JAX-RS interceptors, double submit implementation
- JavaScript/Node.js (Express, Next.js, React) - Express csurf middleware, Fastify CSRF plugin, Next.js middleware, React/Axios integration
- C# (ASP.NET Core, ASP.NET MVC) - Anti-forgery tokens, ValidateAntiForgeryToken attribute, Razor Pages, Blazor Server
Each language-specific guide includes framework-specific patterns, vulnerable/secure code examples, testing approaches, and common pitfalls to avoid.
Dynamic Scan Guidance
For guidance on remediating this CWE when detected by dynamic (DAST) scanners:
- Dynamic Scan Guidance - Analyzing DAST findings and mapping to source code