Skip to content

Commit 42d2b15

Browse files
committed
Merge branch '2.7'
* 2.7: Re-wording based on Wouter's recommendation update text to use SetHandler (not ProxyPassMatch) cache_csrf_form [Book][Security] Add isPasswordValid doc as in 2.6 [#4656] Re-reading private service section Update code example to fit description
2 parents 6f9832d + e5a6c7c commit 42d2b15

File tree

7 files changed

+84
-34
lines changed

7 files changed

+84
-34
lines changed

book/forms.rst

+9
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,13 @@ section.
17861786
The ``intention`` option is optional but greatly enhances the security of
17871787
the generated token by making it different for each form.
17881788

1789+
.. caution::
1790+
1791+
CSRF tokens are meant to be different for every user. This is why you
1792+
need to be cautious if you try to cache pages with forms including this
1793+
kind of protection. For more information, see
1794+
:doc:`/cookbook/cache/form_csrf_caching`.
1795+
17891796
.. index::
17901797
single: Forms; With no class
17911798

@@ -1921,6 +1928,8 @@ Learn more from the Cookbook
19211928
* :doc:`/cookbook/form/form_customization`
19221929
* :doc:`/cookbook/form/dynamic_form_modification`
19231930
* :doc:`/cookbook/form/data_transformers`
1931+
* :doc:`/cookbook/security/csrf_in_login_form`
1932+
* :doc:`/cookbook/cache/form_csrf_caching`
19241933

19251934
.. _`Symfony Form component`: https://github.com/symfony/Form
19261935
.. _`DateTime`: http://php.net/manual/en/class.datetime.php

book/security.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1174,8 +1174,8 @@ in the following way from a controller::
11741174
// whatever *your* User object is
11751175
$user = new AppBundle\Entity\User();
11761176
$plainPassword = 'ryanpass';
1177-
$encoded = $this->container->get('security.password_encoder')
1178-
->encodePassword($user, $plainPassword);
1177+
$encoder = $this->container->get('security.password_encoder');
1178+
$encoded = $encoder->encodePassword($user, $plainPassword);
11791179

11801180
$user->setPassword($encoded);
11811181

components/dependency_injection/advanced.rst

+17-19
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,19 @@ Marking Services as public / private
1010
When defining services, you'll usually want to be able to access these definitions
1111
within your application code. These services are called ``public``. For example,
1212
the ``doctrine`` service registered with the container when using the DoctrineBundle
13-
is a public service as you can access it via::
13+
is a public service. This means that you can fetch it from the container
14+
using the ``get()`` method::
1415

1516
$doctrine = $container->get('doctrine');
1617

17-
However, there are use-cases when you don't want a service to be public. This
18-
is common when a service is only defined because it could be used as an
19-
argument for another service.
18+
In some cases, a service *only* exists to be injected into another service
19+
and is *not* intended to be fetched directly from the container as shown
20+
above.
2021

2122
.. _inlined-private-services:
2223

23-
Since a container is not able to detect if a service is retrieved from inside
24-
the container or the outside, a private service may still be retrieved using
25-
the ``get()`` method.
26-
27-
What makes private services special, is that they are converted from services
28-
to inlined instantiation (e.g. ``new PrivateThing()``) when they are only
29-
injected once, to increase the container performance. This means that you can
30-
never be sure if a private service exists in the container.
31-
32-
Simply said: A service will be private when you do not want to access it
33-
directly from your code.
34-
35-
Here is an example:
24+
In these cases, to get a minor performance boost, you can set the service
25+
to be *not* public (i.e. private):
3626

3727
.. configuration-block::
3828

@@ -63,11 +53,19 @@ Here is an example:
6353
$definition->setPublic(false);
6454
$container->setDefinition('foo', $definition);
6555
66-
Now that the service is private, you *should not* call (should not means, this
67-
*might* fail, see the explaination above)::
56+
What makes private services special is that, if they are only injected once,
57+
they are converted from services to inlined instantiations (e.g. ``new PrivateThing()``).
58+
This increases the container's performance.
59+
60+
Now that the service is private, you *should not* fetch the service directly
61+
from the container::
6862

6963
$container->get('foo');
7064

65+
This *may or may not work*, depending on if the service could be inlined.
66+
Simply said: A service can be marked as private if you do not want to access
67+
it directly from your code.
68+
7169
However, if a service has been marked as private, you can still alias it (see
7270
below) to access this service (via the alias).
7371

cookbook/cache/form_csrf_caching.rst

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
.. index::
2+
single: Cache; CSRF; Forms
3+
4+
Caching Pages that Contain CSRF Protected Forms
5+
===============================================
6+
7+
CSRF tokens are meant to be different for every user. This is why you
8+
need to be cautious if you try to cache pages with forms including them.
9+
10+
For more information about how CSRF protection works in Symfony, please
11+
check :ref:`CSRF Protection <forms-csrf>`.
12+
13+
Why Reverse Proxy Caches do not Cache these Pages by Default
14+
------------------------------------------------------------
15+
16+
There are many ways to generate unique tokens for each user but in order get
17+
them validated when the form is submitted, you need to store them inside the
18+
PHP Session.
19+
20+
If you are using Varnish or some similar reverse proxy cache and you try to cache
21+
pages containing forms with CSRF token protection, you will see that, by default,
22+
the reverse proxy cache refuses to cache.
23+
24+
This happens because a cookie is sent in order to preserve the PHP session open and
25+
Varnish default behaviour is to not cache HTTP requests with cookies.
26+
27+
If you think about it, if you managed to cache the form you would end up
28+
with many users getting the same token in the form generation. When these
29+
users try to send the form to the server, the CSRF validation will fail for
30+
them because the expected token is stored in their session and different
31+
for each user.
32+
33+
How to Cache Most of the Page and still Be Able to Use CSRF Protection
34+
----------------------------------------------------------------------
35+
36+
To cache a page that contains a CSRF token you can use more advanced caching
37+
techniques like `ESI`_ fragments, having a TTL for the full page and embedding
38+
the form inside an ESI tag with no cache at all.
39+
40+
Another option to be able to cache that heavy page would be loading the form
41+
via an uncached AJAX request but cache the rest of the HTML response.
42+
43+
Or you can even load just the CSRF token with an AJAX request and replace the
44+
form field value with it.
45+
46+
.. _`Cross-site request forgery`: http://en.wikipedia.org/wiki/Cross-site_request_forgery
47+
.. _`ESI`: http://www.w3.org/TR/esi-lang
48+
.. _`Security CSRF Component`: https://github.com/symfony/security-csrf

cookbook/cache/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ Cache
55
:maxdepth: 2
66

77
varnish
8+
form_csrf_caching

cookbook/configuration/web_server_configuration.rst

+6-13
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Using mod_proxy_fcgi with Apache 2.4
118118
If you are running Apache 2.4, you can easily use ``mod_proxy_fcgi`` to pass
119119
incoming requests to PHP-FPM. Configure PHP-FPM to listen on a TCP socket
120120
(``mod_proxy`` currently `does not support unix sockets`_), enable ``mod_proxy``
121-
and ``mod_proxy_fcgi`` in your Apache configuration and use the ``ProxyPassMatch``
121+
and ``mod_proxy_fcgi`` in your Apache configuration and use the ``SetHandler``
122122
directive to pass requests for PHP files to PHP FPM:
123123

124124
.. code-block:: apache
@@ -133,14 +133,17 @@ directive to pass requests for PHP files to PHP FPM:
133133
# SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
134134
135135
# For Apache 2.4.9 or higher
136-
# Using SetHandler avoids issues with using ProxyPassMatch in combination
136+
# Using SetHandler avoids issues with using ProxyPassMatch in combination
137137
# with mod_rewrite or mod_autoindex
138138
<FilesMatch \.php$>
139139
SetHandler proxy:fcgi://127.0.0.1:9000
140140
</FilesMatch>
141141
# If you use Apache version below 2.4.9 you must consider update or use this instead
142142
# ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1
143-
143+
# If you run your Symfony application on a subpath of your document root, the
144+
# regular expression must be changed accordingly:
145+
# ProxyPassMatch ^/path-to-app/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1
146+
144147
DocumentRoot /var/www/project/web
145148
<Directory /var/www/project/web>
146149
# enable the .htaccess rewrites
@@ -152,16 +155,6 @@ directive to pass requests for PHP files to PHP FPM:
152155
CustomLog /var/log/apache2/project_access.log combined
153156
</VirtualHost>
154157
155-
.. caution::
156-
157-
When you run your Symfony application on a subpath of your document root,
158-
the regular expression used in ``ProxyPassMatch`` directive must be changed
159-
accordingly:
160-
161-
.. code-block:: apache
162-
163-
ProxyPassMatch ^/path-to-app/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/var/www/project/web/$1
164-
165158
PHP-FPM with Apache 2.2
166159
~~~~~~~~~~~~~~~~~~~~~~~
167160

cookbook/map.rst.inc

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
* :doc:`/cookbook/cache/index`
2121

2222
* :doc:`/cookbook/cache/varnish`
23+
* :doc:`/cookbook/cache/form_csrf_caching`
2324

2425
* **Composer**
2526

0 commit comments

Comments
 (0)