Symfony 7.2 introduces a significant enhancement to its security features: Stateless CSRF (Cross-Site Request Forgery) protection. This new feature uses a combination of cookies and HTTP headers to validate non-persistent tokens.
The main advantage of this feature over the traditional CSRF techniques is that tokens are validated without relying on server-side sessions. This makes it suitable for applications that use HTTP caching. Additionally, not relying on sessions ensures that users won't lose their content if they take some time to submit a form. For example, if the session is destroyed while they are filling in their form, the "Remember Me" feature will reconnect them, and the form submission will still be accepted.
Enabling Stateless CSRF
To enable Stateless CSRF protection in Symfony 7.2, update your Symfony configuration as follows:
1 2 3 4
# config/packages/framework.yaml
framework:
csrf_protection:
stateless_token_ids: ['my_stateless_token_id']
The stateless_token_ids
option is one of the parts that make the stateless
CSRF feature as safe as the traditional CSRF feature, because it explicitly lists
the token IDs allowed when using the new feature.
The following options have also been added under the csrf_protection
option:
cookie_name
: the name of the cookie to use (default:csrf-token
);check_header
: iftrue
, the CSRF token is checked in an HTTP header in addition to a cookie (default:false
).
How Does Stateless CSRF Work?
First, the source of the request is validated using the Origin
/Referer
HTTP headers. This relies on the application being able to determine its own
target origin. If you are behind a reverse proxy, don't forget to configure your reverse proxy
to send the appropriate X-Forwarded-*
/Forwarded
HTTP headers.
Next, the request is validated using a cookie and a CsrfToken
. If the
cookie is found, it must contain the same value as the CsrfToken
. A
JavaScript snippet on the client side is responsible for performing this
double-submission. The token value should be regenerated on every request
using a cryptographically secure random generator.
If either the double-submit mechanism or Origin
/Referer
HTTP headers
are missing, it typically indicates that JavaScript is disabled on the client
side, the JavaScript snippet was not properly implemented, or the
Origin
/Referer
headers were filtered out. Requests lacking both
double-submit and origin information are considered insecure.
New Symfony applications that use Symfony Flex will have this stateless CSRF feature enabled by default, thanks to an updated recipe that adds the following configuration:
1 2 3 4 5
# config/packages/framework.yaml
framework:
form: { csrf_protection: { token_id: 'submit' } }
csrf_protection:
stateless_token_ids: ['submit', 'authenticate', 'logout']
I would like to know / read a lot more about this.
Why is Javascript needed? How does it work (the section 'how does it work' doesn't explain a lot).
Does the old system still work, and is it still secure? Is this for regular html forms submission or also for stateless APIs?
The best improvement on 7.2 in my opinion. Great work @Nicolas Grekas
@Joris the old system still works and it is still secure. But it relies on a server-side session, which means that the page becomes uncacheable (and that the CSRF token is considered invalid if the session has expired)
Really intresting new feature but I like to know more about how this works. Is there any more documentation available or PR to look into?
For testing purpose, the CSRF token is variable in pages : it could be useful if we compares the page HTML render at many moments with a CSRF, thanks !
Interesting implementation with the Stimulus controller! I'm curious how it works with my conditional forms, where I remove the CSRF token via JavaScript before submitting to invalidate the form and reload specific parts. It’ll be interesting to see how it fits together. Great solution for caching and expired sessions!
I would love to get more documentation about the related Stimulus controller added by the Flex recipe :)