This document analyzes the OWASP security concerns raised during the development of Symfony's stateless CSRF protection feature, based on GitHub PR #58095.
- Issue: Pointed out that OWASP discourages the naive double-submit cookie pattern
- Reference: OWASP CSRF Prevention Cheat Sheet - Naive Double-Submit Cookie Pattern (DISCOURAGED)
- Recommendation: Use HMAC/crypted values coupled to sessions instead
Quote from discussion:
"OWASP discourage using this pattern and recommands using a hash/crypted value coupled to the session (or JWT lifetime) instead"
Nicolas argues that Symfony's implementation is fundamentally different from what OWASP discourages:
"I've read that, and what I'm proposing here is significantly different from what they discourage to be considered seriously IMHO, esp with the new cookie attributes and with the style of double-submit I'm using here (which is not discussed on that page)."
The OWASP page links to David Johansson's presentation about vulnerabilities. Nicolas addresses each:
"I read that doc too, and they don't apply to this PR:
• MITM are ruled out by HTTPS nowadays (and no CSRF techniques can resist to MITM anyway) • HTTPS is enforced for cookies by the
__Host-prefix, which prevents overriding them from HTTP • overriding the cookie isn't enough since the double-submit happens via a custom header (which is listed as a valid CSRF-protection on that PDF and on the OWASP page) • the Origin header is checked also when available"
"Binding CSRF tokens to any server-side state is ruling out statelessness/cacheability of the strategy so I'm not looking for anything hashed/signed/etc."
- First line of defense using Origin header validation
- Most secure approach when headers are available
- Used when Origin headers are unavailable (corporate environments, etc.)
- Enhanced with modern security features
__Host-cookie prefix: Prevents subdomain cookie injection on HTTPS- SameSite=Strict cookies: Strengthens CSRF protection
- Custom headers: Harder for attackers to forge than simple cookies
- Token clearing: Token cleared after each response to prevent reuse
"this double-submit is not only a fallback. It's also a defense in-depth. What I mean is that if
Originis broken for any reasons, users won't be immediately exposed."
"When a session is found, a behavioral check is added to ensure that the validation method does not downgrade from double-submit to origin checks. This prevents attackers from exploiting potentially less secure validation methods once a more secure method has been confirmed as functional."
- On HTTPS:
__Host-prefix prevents HTTP channel forgery samesite=strictattribute for additional protection- Cookie cleared on response to prevent token reuse
- Custom header requirement makes cross-origin attacks harder
- Cannot be set programmatically from malicious sites due to CORS
- Token value regenerated on every request
- Uses cryptographically secure random generator
- Token placed in cookie name to prevent race conditions
Original: "Double Submit CSRF Protection" Final: "Stateless Headers/Cookies-based CSRF Protection"
Reason for name change (@stof):
"One argument for renaming: it will avoid getting more reports in the future from people telling us that OWASP discourages the double submit pattern, because they miss that they only discourage naive double-submit."
Multiple Symfony security team members (@wouterj, @chalasr, @stof) reviewed and approved the approach after thorough analysis.
| OWASP Naive Pattern (Discouraged) | Symfony's Implementation |
|---|---|
| Simple cookie matching | Origin header validation + fallback |
| Vulnerable to subdomain attacks | __Host- prefix prevents subdomain injection |
| Only cookie-based validation | Custom header requirement |
| No additional protections | SameSite, HTTPS enforcement, token clearing |
| Pure client-side generation | Server-controlled validation logic |
| Single validation method | Multi-layer defense approach |
This discussion demonstrates thoughtful security engineering that:
- Acknowledges OWASP guidelines while understanding their specific context
- Addresses root attack vectors rather than following rules blindly
- Implements defense-in-depth rather than relying on single protection
- Balances security with architectural needs (statelessness, HTTP caching compatibility)
Key Insight: OWASP discourages "naive" double-submit, but Symfony's implementation adds multiple security layers that specifically address the vulnerabilities OWASP was concerned about. This represents an evolution beyond the basic pattern that OWASP analyzed.
The implementation shows how careful engineering can mitigate security concerns while meeting modern architectural requirements for stateless, scalable applications.