Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0f80916

Browse files
committed
feature#6554 [Security] Added Security\Csrf sub-component with better token generation (bschussek)
This PR was merged into the master branch. Discussion ---------- [Security] Added Security\Csrf sub-component with better token generation | Q | A | ------------- | --- | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | TODO **Update September 27, 2013** This PR simplifies the CSRF mechanism to generate completely random tokens. A random token is generated once per ~~intention~~ token ID and then stored in the session. Tokens are valid until the session expires. Since the CSRF token generator depends on `StringUtils` and `SecureRandom` from Security\Core, and since Security\Http currently depends on the Form component for token generation, I decided to add a new Security\Csrf sub-component that contains the improved CSRF token generator. Consequences: * Security\Http now depends on Security\Csrf instead of Form * Form now optionally depends on Security\Csrf * The configuration for the "security.secure_random" service and the "security.csrf.*" services was moved to FrameworkBundle to guarantee BC In the new Security\Csrf sub-component, I tried to improve the naming where I could do so without breaking BC: * CSRF "providers" are now called "token generators" * CSRF "intentions" are now called "token IDs", because that's really what they are ##### TODO - [ ] The documentation needs to be checked for references to the configuration of the application secret. Remarks that the secret is used for CSRF protection need to be removed. - [ ] Add aliases "csrf_token_generator" and "csrf_token_id" for "csrf_provider" and "intention" in the SecurityBundle configuration - [x] Make sure `SecureRandom` never blocks for `CsrfTokenGenerator` Commits ------- 7f02304 [Security] Added missing PHPDoc tag 2e04e32 Updated Composer dependencies to require the Security\Csrf component where necessary bf85e83 [FrameworkBundle][SecurityBundle] Added service configuration for the new Security CSRF sub-component 2048cf6 [Form] Deprecated the CSRF implementation and added an optional dependency to the Security CSRF sub-component instead 85d4959 [Security] Changed Security HTTP sub-component to depend on CSRF sub-component instead of Form 1bf1640 [Security] Added CSRF sub-component
2 parents 164c1cb + 7f02304 commit 0f80916

File tree

55 files changed

+1158
-178
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1158
-178
lines changed

UPGRADE-3.0.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ UPGRADE FROM 2.x to 3.0
168168
`ChoiceListInterface::getChoicesForValues()` and
169169
`ChoiceListInterface::getValuesForChoices()` should be sufficient.
170170

171+
* The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface`
172+
and all of its implementations were removed. Use the new interface
173+
`Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface` instead.
174+
175+
* The options "csrf_provider" and "intention" were renamed to "csrf_token_generator"
176+
and "csrf_token_id".
177+
171178

172179
### FrameworkBundle
173180

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
"symfony/security": "self.version",
5353
"symfony/security-acl": "self.version",
5454
"symfony/security-core": "self.version",
55+
"symfony/security-csrf": "self.version",
5556
"symfony/security-http": "self.version",
5657
"symfony/security-bundle": "self.version",
5758
"symfony/serializer": "self.version",

src/Symfony/Bridge/Twig/Form/TwigRenderer.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
namespace Symfony\Bridge\Twig\Form;
1313

1414
use Symfony\Component\Form\FormRenderer;
15-
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
15+
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
1616

1717
/**
1818
* @author Bernhard Schussek <[email protected]>
@@ -24,9 +24,9 @@ class TwigRenderer extends FormRenderer implements TwigRendererInterface
2424
*/
2525
private $engine;
2626

27-
public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
27+
public function __construct(TwigRendererEngineInterface $engine, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
2828
{
29-
parent::__construct($engine, $csrfProvider);
29+
parent::__construct($engine, $csrfTokenGenerator);
3030

3131
$this->engine = $engine;
3232
}

src/Symfony/Bridge/Twig/composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
],
1818
"require": {
1919
"php": ">=5.3.3",
20+
"symfony/security-csrf": "~2.4",
2021
"twig/twig": "~1.11"
2122
},
2223
"require-dev": {
@@ -26,7 +27,7 @@
2627
"symfony/templating": "~2.1",
2728
"symfony/translation": "~2.2",
2829
"symfony/yaml": "~2.0",
29-
"symfony/security": "~2.0",
30+
"symfony/security": "~2.4",
3031
"symfony/stopwatch": "~2.2"
3132
},
3233
"suggest": {

src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ CHANGELOG
66

77
* allowed multiple IP addresses in profiler matcher settings
88
* added stopwatch helper to time templates with the WebProfilerBundle
9+
* added service definition for "security.secure_random" service
10+
* added service definitions for the new Security CSRF sub-component
911

1012
2.3.0
1113
-----

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ public function load(array $configs, ContainerBuilder $container)
5656

5757
$loader->load('debug_prod.xml');
5858

59+
// Enable services for CSRF protection (even without forms)
60+
$loader->load('security.xml');
61+
$loader->load('security_csrf.xml');
62+
5963
if ($container->getParameter('kernel.debug')) {
6064
$loader->load('debug.xml');
6165

@@ -158,9 +162,7 @@ private function registerFormConfiguration($config, ContainerBuilder $container,
158162
if (!isset($config['session'])) {
159163
throw new \LogicException('CSRF protection needs that sessions are enabled.');
160164
}
161-
if (!isset($config['secret'])) {
162-
throw new \LogicException('CSRF protection needs a secret to be set.');
163-
}
165+
164166
$loader->load('form_csrf.xml');
165167

166168
$container->setParameter('form.type_extension.csrf.enabled', true);

src/Symfony/Bundle/FrameworkBundle/Resources/config/form_csrf.xml

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,8 @@
44
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
66

7-
<parameters>
8-
<parameter key="form.csrf_provider.class">Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider</parameter>
9-
</parameters>
10-
117
<services>
12-
<service id="form.csrf_provider" class="%form.csrf_provider.class%">
13-
<argument type="service" id="session" />
14-
<argument>%kernel.secret%</argument>
15-
</service>
8+
<service id="form.csrf_provider" class="Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfTokenGeneratorAdapter" parent="security.csrf.token_generator" />
169

1710
<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
1811
<tag name="form.type_extension" alias="form" />
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<parameters>
8+
<parameter key="security.secure_random.class">Symfony\Component\Security\Core\Util\SecureRandom</parameter>
9+
</parameters>
10+
11+
<services>
12+
<!-- Pseudo-Random Number Generator -->
13+
<service id="security.secure_random" class="%security.secure_random.class%">
14+
<tag name="monolog.logger" channel="security" />
15+
<argument>%kernel.cache_dir%/secure_random.seed</argument>
16+
<argument type="service" id="logger" on-invalid="ignore" />
17+
</service>
18+
</services>
19+
</container>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<parameters>
8+
<parameter key="security.csrf.token_generator.class">Symfony\Component\Security\Csrf\CsrfTokenGenerator</parameter>
9+
<parameter key="security.csrf.token_storage.class">Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage</parameter>
10+
</parameters>
11+
12+
<services>
13+
<service id="security.csrf.token_storage" class="%security.csrf.token_storage.class%" public="false">
14+
<argument type="service" id="session" />
15+
</service>
16+
17+
<service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%">
18+
<argument type="service" id="security.csrf.token_storage" />
19+
<argument type="service" id="security.secure_random" />
20+
</service>
21+
</services>
22+
</container>

src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public function block(FormView $view, $blockName, array $variables = array())
247247
* Check the token in your action using the same intention.
248248
*
249249
* <code>
250-
* $csrfProvider = $this->get('form.csrf_provider');
250+
* $csrfProvider = $this->get('security.csrf.token_generator');
251251
* if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) {
252252
* throw new \RuntimeException('CSRF attack detected.');
253253
* }

src/Symfony/Bundle/FrameworkBundle/Tests/DependencyInjection/FrameworkExtensionTest.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ public function testCsrfProtection()
3030
$this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1));
3131
$this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name'));
3232
$this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2));
33-
$this->assertEquals('s3cr3t', $container->getParameterBag()->resolveValue($container->findDefinition('form.csrf_provider')->getArgument(1)));
3433
}
3534

3635
public function testProxies()

src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperDivLayoutTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ protected function getExtensions()
4646
));
4747

4848
return array_merge(parent::getExtensions(), array(
49-
new TemplatingExtension($this->engine, $this->csrfProvider, array(
49+
new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
5050
'FrameworkBundle:Form',
5151
)),
5252
));

src/Symfony/Bundle/FrameworkBundle/Tests/Templating/Helper/FormHelperTableLayoutTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ protected function getExtensions()
4646
));
4747

4848
return array_merge(parent::getExtensions(), array(
49-
new TemplatingExtension($this->engine, $this->csrfProvider, array(
49+
new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
5050
'FrameworkBundle:Form',
5151
'FrameworkBundle:FormTable',
5252
)),

src/Symfony/Bundle/FrameworkBundle/composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,16 @@
2323
"symfony/http-kernel": "~2.3",
2424
"symfony/filesystem": "~2.3",
2525
"symfony/routing": "~2.2",
26+
"symfony/security-core": "~2.4",
27+
"symfony/security-csrf": "~2.4",
2628
"symfony/stopwatch": "~2.3",
2729
"symfony/templating": "~2.1",
2830
"symfony/translation": "~2.3",
2931
"doctrine/common": "~2.2"
3032
},
3133
"require-dev": {
3234
"symfony/finder": "~2.0",
33-
"symfony/security": "~2.3",
35+
"symfony/security": "~2.4",
3436
"symfony/form": "~2.3",
3537
"symfony/class-loader": "~2.1",
3638
"symfony/validator": "~2.1"

src/Symfony/Bundle/SecurityBundle/CHANGELOG.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* Added 'host' option to firewall configuration
8+
* Moved 'security.secure_random' service configuration to FrameworkBundle
89

910
2.3.0
1011
-----
@@ -79,9 +80,9 @@ CHANGELOG
7980
logout:
8081
path: /logout_path
8182
target: /
82-
csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token")
83-
csrf_provider: form.csrf_provider # Required to enable protection
84-
intention: logout # Optional (defaults to "logout")
83+
csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token")
84+
csrf_provider: security.csrf.token_generator # Required to enable protection
85+
intention: logout # Optional (defaults to "logout")
8586
```
8687

8788
If the LogoutListener has CSRF protection enabled but cannot validate a token,

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,5 @@
151151
<argument type="service" id="security.context" />
152152
<argument type="service" id="security.encoder_factory" />
153153
</service>
154-
155-
<!-- Pseudorandom Number Generator -->
156-
<service id="security.secure_random" class="Symfony\Component\Security\Core\Util\SecureRandom">
157-
<tag name="monolog.logger" channel="security" />
158-
<argument>%kernel.cache_dir%/secure_random.seed</argument>
159-
<argument type="service" id="logger" on-invalid="ignore" />
160-
</service>
161154
</services>
162155
</container>

src/Symfony/Bundle/SecurityBundle/Templating/Helper/LogoutUrlHelper.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;
1313

1414
use Symfony\Component\DependencyInjection\ContainerInterface;
15-
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
1615
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
16+
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
1717
use Symfony\Component\Templating\Helper\Helper;
1818

1919
/**
@@ -43,15 +43,15 @@ public function __construct(ContainerInterface $container, UrlGeneratorInterface
4343
/**
4444
* Registers a firewall's LogoutListener, allowing its URL to be generated.
4545
*
46-
* @param string $key The firewall key
47-
* @param string $logoutPath The path that starts the logout process
48-
* @param string $intention The intention for CSRF token generation
49-
* @param string $csrfParameter The CSRF token parameter name
50-
* @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
46+
* @param string $key The firewall key
47+
* @param string $logoutPath The path that starts the logout process
48+
* @param string $csrfTokenId The ID of the CSRF token
49+
* @param string $csrfParameter The CSRF token parameter name
50+
* @param CsrfTokenGeneratorInterface $csrfTokenGenerator A CsrfTokenGeneratorInterface instance
5151
*/
52-
public function registerListener($key, $logoutPath, $intention, $csrfParameter, CsrfProviderInterface $csrfProvider = null)
52+
public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
5353
{
54-
$this->listeners[$key] = array($logoutPath, $intention, $csrfParameter, $csrfProvider);
54+
$this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenGenerator);
5555
}
5656

5757
/**
@@ -94,9 +94,9 @@ private function generateLogoutUrl($key, $referenceType)
9494
throw new \InvalidArgumentException(sprintf('No LogoutListener found for firewall key "%s".', $key));
9595
}
9696

97-
list($logoutPath, $intention, $csrfParameter, $csrfProvider) = $this->listeners[$key];
97+
list($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenGenerator) = $this->listeners[$key];
9898

99-
$parameters = null !== $csrfProvider ? array($csrfParameter => $csrfProvider->generateCsrfToken($intention)) : array();
99+
$parameters = null !== $csrfTokenGenerator ? array($csrfParameter => $csrfTokenGenerator->generateCsrfToken($csrfTokenId)) : array();
100100

101101
if ('/' === $logoutPath[0]) {
102102
$request = $this->container->get('request');

src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/CsrfFormLogin/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ security:
3737
username_parameter: "user_login[username]"
3838
password_parameter: "user_login[password]"
3939
csrf_parameter: "user_login[_token]"
40-
csrf_provider: form.csrf_provider
40+
csrf_provider: security.csrf.token_generator
4141
anonymous: ~
4242
logout:
4343
path: /logout_path
4444
target: /
45-
csrf_provider: form.csrf_provider
45+
csrf_provider: security.csrf.token_generator
4646

4747
access_control:
4848
- { path: .*, roles: IS_AUTHENTICATED_FULLY }

src/Symfony/Bundle/SecurityBundle/composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
],
1818
"require": {
1919
"php": ">=5.3.3",
20-
"symfony/security": "~2.2",
20+
"symfony/security": "~2.4",
2121
"symfony/http-kernel": "~2.2"
2222
},
2323
"require-dev": {
2424
"symfony/framework-bundle": "~2.2",
2525
"symfony/twig-bundle": "~2.2",
26-
"symfony/form": "~2.1",
2726
"symfony/validator": "~2.2",
2827
"symfony/yaml": "~2.0",
2928
"symfony/expression-language": "~2.4"

src/Symfony/Component/Form/CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
CHANGELOG
22
=========
33

4+
2.4.0
5+
-----
6+
7+
* moved CSRF implementation to the new Security CSRF sub-component
8+
* deprecated CsrfProviderInterface and its implementations
9+
* deprecated options "csrf_provider" and "intention" in favor of the new options "csrf_token_generator" and "csrf_token_id"
410

511
2.3.0
6-
------
12+
-----
713

814
* deprecated FormPerformanceTestCase and FormIntegrationTestCase in the Symfony\Component\Form\Tests namespace and moved them to the Symfony\Component\Form\Test namespace
915
* deprecated TypeTestCase in the Symfony\Component\Form\Tests\Extension\Core\Type namespace and moved it to the Symfony\Component\Form\Test namespace

src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
namespace Symfony\Component\Form\Extension\Csrf;
1313

1414
use Symfony\Component\Form\Extension\Csrf\Type;
15-
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
1615
use Symfony\Component\Form\AbstractExtension;
16+
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
1717
use Symfony\Component\Translation\TranslatorInterface;
1818

1919
/**
@@ -24,9 +24,9 @@
2424
class CsrfExtension extends AbstractExtension
2525
{
2626
/**
27-
* @var CsrfProviderInterface
27+
* @var CsrfTokenGeneratorInterface
2828
*/
29-
private $csrfProvider;
29+
private $tokenGenerator;
3030

3131
/**
3232
* @var TranslatorInterface
@@ -41,13 +41,13 @@ class CsrfExtension extends AbstractExtension
4141
/**
4242
* Constructor.
4343
*
44-
* @param CsrfProviderInterface $csrfProvider The CSRF provider
45-
* @param TranslatorInterface $translator The translator for translating error messages.
46-
* @param null|string $translationDomain The translation domain for translating.
44+
* @param CsrfTokenGeneratorInterface $tokenGenerator The CSRF token generator
45+
* @param TranslatorInterface $translator The translator for translating error messages
46+
* @param null|string $translationDomain The translation domain for translating
4747
*/
48-
public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInterface $translator = null, $translationDomain = null)
48+
public function __construct(CsrfTokenGeneratorInterface $tokenGenerator, TranslatorInterface $translator = null, $translationDomain = null)
4949
{
50-
$this->csrfProvider = $csrfProvider;
50+
$this->tokenGenerator = $tokenGenerator;
5151
$this->translator = $translator;
5252
$this->translationDomain = $translationDomain;
5353
}
@@ -58,7 +58,7 @@ public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInter
5858
protected function loadTypeExtensions()
5959
{
6060
return array(
61-
new Type\FormTypeCsrfExtension($this->csrfProvider, true, '_token', $this->translator, $this->translationDomain),
61+
new Type\FormTypeCsrfExtension($this->tokenGenerator, true, '_token', $this->translator, $this->translationDomain),
6262
);
6363
}
6464
}

0 commit comments

Comments
 (0)