@@ -34,9 +34,9 @@ unique tokens added to forms as hidden fields. The legit server validates them t
34
34
ensure that the request originated from the expected source and not some other
35
35
malicious website.
36
36
37
- Anti-CSRF tokens can be managed either in a stateful way: they're put in the
38
- session and are unique for each user and for each kind of action, or in a
39
- stateless way: they're generated on the client- side.
37
+ Anti-CSRF tokens can be managed in two ways: using a ** stateful ** approach,
38
+ where tokens are stored in the session and are unique per user and action; or a
39
+ ** stateless ** approach, where tokens are generated on the client side.
40
40
41
41
Installation
42
42
------------
@@ -106,7 +106,7 @@ protected forms, among them:
106
106
field value with it.
107
107
108
108
The most effective way to cache pages that need CSRF protected forms is to use
109
- stateless CSRF tokens, see below.
109
+ :ref: ` stateless CSRF tokens < csrf-stateless-tokens >`, as explained below.
110
110
111
111
.. _csrf-protection-forms :
112
112
@@ -310,6 +310,8 @@ targeted parts of the plaintext. To mitigate these attacks, and prevent an
310
310
attacker from guessing the CSRF tokens, a random mask is prepended to the token
311
311
and used to scramble it.
312
312
313
+ .. _csrf-stateless-tokens :
314
+
313
315
Stateless CSRF Tokens
314
316
---------------------
315
317
@@ -363,28 +365,31 @@ option:
363
365
;
364
366
};
365
367
366
- Stateless CSRF tokens use a CSRF protection that doesn't need the session. This
367
- means that you can cache the entire page and still have CSRF protection .
368
+ Stateless CSRF tokens provide protection without relying on the session. This
369
+ allows you to fully cache pages while still protecting against CSRF attacks .
368
370
369
- When a stateless CSRF token is checked for validity, Symfony verifies the
370
- ``Origin `` and the ``Referer `` headers of the incoming HTTP request.
371
+ When validating a stateless CSRF token, Symfony checks the ``Origin `` and
372
+ ``Referer `` headers of the incoming HTTP request. If either header matches the
373
+ application's target origin (i.e. its domain), the token is considered valid.
371
374
372
- If either of these headers match the target origin of the application (its domain
373
- name), the CSRF token is considered valid. This relies on the app being able to
374
- know its own target origin. Don't miss configuring your reverse proxy if you're
375
- behind one. See :doc: `/deployment/proxies `.
375
+ This mechanism relies on the application being able to determine its own origin.
376
+ If you're behind a reverse proxy, make sure it's properly configured. See
377
+ :doc: `/deployment/proxies `.
376
378
377
379
Using a Default Token ID
378
380
~~~~~~~~~~~~~~~~~~~~~~~~
379
381
380
- While stateful CSRF tokens are better seggregated per form or action, stateless
381
- ones don't need many token identifiers. In the previous example, ``authenticate ``
382
- and ``logout `` are listed because they're the default identifiers used by the
383
- Symfony Security component. The ``submit `` identifier is then listed so that
384
- form types defined by the application can use it by default. The following
385
- configuration - which applies only to form types declared using autofiguration
386
- (the default way to declare *your * services) - will make your form types use the
387
- ``submit `` token identifier by default:
382
+ Stateful CSRF tokens are typically scoped per form or action, while stateless
383
+ tokens don't require many identifiers.
384
+
385
+ In the example above, the ``authenticate `` and ``logout `` identifiers are listed
386
+ because they are used by default in the Symfony Security component. The ``submit ``
387
+ identifier is included so that form types defined by the application can also use
388
+ CSRF protection by default.
389
+
390
+ The following configuration applies only to form types registered via
391
+ :ref: `autoconfiguration <services-autoconfigure >` (which is the default for your
392
+ own services), and it sets ``submit `` as their default token identifier:
388
393
389
394
.. configuration-block ::
390
395
@@ -433,41 +438,40 @@ option will use the stateless CSRF protection.
433
438
Generating CSRF Token Using Javascript
434
439
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435
440
436
- In addition to the ``Origin `` and ``Referer `` headers, stateless CSRF protection
437
- also checks a cookie and a header (named ``csrf-token `` by default, see the
438
- :ref: `CSRF configuration reference <reference-framework-csrf-protection >`).
439
-
440
- These extra checks are part of defense-in-depth strategies provided by the
441
- stateless CSRF protection. They are optional and they require
442
- `some JavaScript `_ to be activated. This JavaScript is responsible for generating
443
- a crypto-safe random token when a form is submitted, then putting the token in
444
- the hidden CSRF field of the form and submitting it also as a cookie and header.
445
- On the server-side, the CSRF token is validated by checking the cookie and header
446
- values. This "double-submit" protection relies on the same-origin policy
447
- implemented by browsers and is strengthened by regenerating the token at every
448
- form submission - which prevents cookie fixation issues - and by using
449
- ``samesite=strict `` and ``__Host- `` cookies, which make them domain-bound and
450
- HTTPS-only.
451
-
452
- Note that the default snippet of JavaScript provided by Symfony requires that
453
- the hidden CSRF form field is either named ``_csrf_token ``, or that it has the
454
- ``data-controller="csrf-protection" `` attribute. You can of course take
455
- inspiration from this snippet to write your own, provided you follow the same
456
- protocol.
457
-
458
- As a last measure, a behavioral check is added on the server-side to ensure that
459
- the validation method cannot be downgraded: if and only if a session is already
460
- available, successful "double-submit" is remembered and is then required for
461
- subsequent requests. This prevents attackers from exploiting potentially reduced
462
- validation checks once cookie and/or header validation has been confirmed as
463
- effective (they're optional by default as explained above).
441
+ In addition to the ``Origin `` and ``Referer `` HTTP headers, stateless CSRF protection
442
+ can also validate tokens using a cookie and a header (named ``csrf-token `` by
443
+ default; see the :ref: `CSRF configuration reference <reference-framework-csrf-protection >`).
444
+
445
+ These additional checks are part of the **defense-in-depth ** strategy provided by
446
+ stateless CSRF protection. They are optional and require `some JavaScript `_ to
447
+ be enabled. This JavaScript generates a cryptographically secure random token
448
+ when a form is submitted. It then inserts the token into the form's hidden CSRF
449
+ field and sends it in both a cookie and a request header.
450
+
451
+ On the server side, CSRF token validation compares the values in the cookie and
452
+ the header. This "double-submit" protection relies on the browser's same-origin
453
+ policy and is further hardened by:
454
+
455
+ * generating a new token for each submission (to prevent cookie fixation);
456
+ * using ``samesite=strict `` and ``__Host- `` cookie attributes (to enforce HTTPS
457
+ and limit the cookie to the current domain).
458
+
459
+ By default, the Symfony JavaScript snippet expects the hidden CSRF field to be
460
+ named ``_csrf_token `` or to include the ``data-controller="csrf-protection" ``
461
+ attribute. You can adapt this logic to your needs as long as the same protocol
462
+ is followed.
463
+
464
+ To prevent validation from being downgraded, an extra behavioral check is performed:
465
+ if (and only if) a session already exists, successful "double-submit" is remembered
466
+ and becomes required for future requests. This ensures that once the optional cookie/header
467
+ validation has been proven effective, it remains enforced for that session.
464
468
465
469
.. note ::
466
470
467
- Enforcing successful "double-submit" for every requests is not recommended as
468
- as it could lead to a broken user experience. The opportunistic approach
469
- described above is preferred because it allows the application to gracefully
470
- degrade to ``Origin `` / ``Referer `` checks when JavaScript is not available .
471
+ Enforcing "double-submit" validation on all requests is not recommended,
472
+ as it may lead to a broken user experience. The opportunistic approach
473
+ described above is preferred, allowing the application to gracefully
474
+ fall back to ``Origin `` / ``Referer `` checks when JavaScript is unavailable .
471
475
472
476
.. _`Cross-site request forgery` : https://en.wikipedia.org/wiki/Cross-site_request_forgery
473
477
.. _`BREACH` : https://en.wikipedia.org/wiki/BREACH
0 commit comments