Nicolas Grekas
Contributed by Nicolas Grekas in #58095

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: if true, 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']
Published in #Living on the edge