Skip to content

Commit e12256c

Browse files
committed
Rewords
1 parent 5a63313 commit e12256c

File tree

2 files changed

+71
-65
lines changed

2 files changed

+71
-65
lines changed

reference/configuration/framework.rst

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -857,36 +857,38 @@ stateless_token_ids
857857

858858
**type**: ``array`` **default**: ``[]``
859859

860-
The list of CSRF token ids that will use stateless CSRF protection.
860+
The list of CSRF token ids that will use :ref:`stateless CSRF protection <csrf-stateless-tokens>`.
861861

862862
.. versionadded:: 7.2
863863

864-
This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection.
864+
The ``stateless_token_ids`` option was introduced in Symfony 7.2.
865865

866866
check_header
867867
............
868868

869869
**type**: ``integer`` or ``bool`` **default**: ``false``
870870

871-
Whether to check the CSRF token in a header in addition to a cookie when using stateless protection.
872-
Can be set to ``2`` (the value of the ``CHECK_ONLY_HEADER`` constant on the
873-
:class:`Symfony\\Component\\Security\\Csrf\\SameOriginCsrfTokenManager` class) to check only the header
874-
and not the cookie.
871+
Whether to check the CSRF token in an HTTP header in addition to the cookie when
872+
using :ref:`stateless CSRF protection <csrf-stateless-tokens>`. You can also set
873+
this to ``2`` (the value of the ``CHECK_ONLY_HEADER`` constant on the
874+
:class:`Symfony\\Component\\Security\\Csrf\\SameOriginCsrfTokenManager` class)
875+
to check only the header and ignore the cookie.
875876

876877
.. versionadded:: 7.2
877878

878-
This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection.
879+
The ``check_header`` option was introduced in Symfony 7.2.
879880

880881
cookie_name
881882
...........
882883

883884
**type**: ``string`` **default**: ``csrf-token``
884885

885-
The name of the cookie (and header) to use for the double-submit when using stateless protection.
886+
The name of the cookie (and HTTP header) to use for the double-submit when using
887+
:ref:`stateless CSRF protection <csrf-stateless-tokens>`.
886888

887889
.. versionadded:: 7.2
888890

889-
This option was added in Symfony 7.2 to aid in configuring stateless CSRF protection.
891+
The ``cookie_name`` option was introduced in Symfony 7.2.
890892

891893
.. _config-framework-default_locale:
892894

@@ -1213,16 +1215,16 @@ field_attr
12131215

12141216
**type**: ``array`` **default**: ``['data-controller' => 'csrf-protection']``
12151217

1216-
This is the HTML attributes that should be added to the CSRF token field of your forms.
1218+
HTML attributes to add to the CSRF token field of your forms.
12171219

12181220
token_id
12191221
''''''''
12201222

12211223
**type**: ``string`` **default**: ``null``
12221224

1223-
This is the CSRF token id that should be used for validating the CSRF tokens of your forms.
1224-
Note that this setting applies only to autoconfigured form types, which usually means only
1225-
to your own form types and not to form types registered by third-party bundles.
1225+
The CSRF token ID used to validate the CSRF tokens of your forms. This setting
1226+
applies only to form types that use :ref:`service autoconfiguration <services-autoconfigure>`,
1227+
which typically means your own form types, not those registered by third-party bundles.
12261228

12271229
fragments
12281230
~~~~~~~~~

security/csrf.rst

Lines changed: 56 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ unique tokens added to forms as hidden fields. The legit server validates them t
3434
ensure that the request originated from the expected source and not some other
3535
malicious website.
3636

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.
4040

4141
Installation
4242
------------
@@ -106,7 +106,7 @@ protected forms, among them:
106106
field value with it.
107107

108108
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.
110110

111111
.. _csrf-protection-forms:
112112

@@ -310,6 +310,8 @@ targeted parts of the plaintext. To mitigate these attacks, and prevent an
310310
attacker from guessing the CSRF tokens, a random mask is prepended to the token
311311
and used to scramble it.
312312

313+
.. _csrf-stateless-tokens:
314+
313315
Stateless CSRF Tokens
314316
---------------------
315317

@@ -363,28 +365,31 @@ option:
363365
;
364366
};
365367
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.
368370

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.
371374

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`.
376378

377379
Using a Default Token ID
378380
~~~~~~~~~~~~~~~~~~~~~~~~
379381

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:
388393

389394
.. configuration-block::
390395

@@ -433,41 +438,40 @@ option will use the stateless CSRF protection.
433438
Generating CSRF Token Using Javascript
434439
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
435440

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.
464468

465469
.. note::
466470

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.
471475

472476
.. _`Cross-site request forgery`: https://en.wikipedia.org/wiki/Cross-site_request_forgery
473477
.. _`BREACH`: https://en.wikipedia.org/wiki/BREACH

0 commit comments

Comments
 (0)