Skip to content

[Security] Added Security\Csrf sub-component with better token generation #6554

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 30, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions UPGRADE-3.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,13 @@ UPGRADE FROM 2.x to 3.0
`ChoiceListInterface::getChoicesForValues()` and
`ChoiceListInterface::getValuesForChoices()` should be sufficient.

* The interface `Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface`
and all of its implementations were removed. Use the new interface
`Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface` instead.

* The options "csrf_provider" and "intention" were renamed to "csrf_token_generator"
and "csrf_token_id".


### FrameworkBundle

Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"symfony/security": "self.version",
"symfony/security-acl": "self.version",
"symfony/security-core": "self.version",
"symfony/security-csrf": "self.version",
"symfony/security-http": "self.version",
"symfony/security-bundle": "self.version",
"symfony/serializer": "self.version",
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Bridge/Twig/Form/TwigRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Symfony\Bridge\Twig\Form;

use Symfony\Component\Form\FormRenderer;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;

/**
* @author Bernhard Schussek <bschussek@gmail.com>
Expand All @@ -24,9 +24,9 @@ class TwigRenderer extends FormRenderer implements TwigRendererInterface
*/
private $engine;

public function __construct(TwigRendererEngineInterface $engine, CsrfProviderInterface $csrfProvider = null)
public function __construct(TwigRendererEngineInterface $engine, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
{
parent::__construct($engine, $csrfProvider);
parent::__construct($engine, $csrfTokenGenerator);

$this->engine = $engine;
}
Expand Down
3 changes: 2 additions & 1 deletion src/Symfony/Bridge/Twig/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
],
"require": {
"php": ">=5.3.3",
"symfony/security-csrf": "~2.4",
"twig/twig": "~1.11"
},
"require-dev": {
Expand All @@ -26,7 +27,7 @@
"symfony/templating": "~2.1",
"symfony/translation": "~2.2",
"symfony/yaml": "~2.0",
"symfony/security": "~2.0",
"symfony/security": "~2.4",
"symfony/stopwatch": "~2.2"
},
"suggest": {
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ CHANGELOG

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

2.3.0
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ public function load(array $configs, ContainerBuilder $container)

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

// Enable services for CSRF protection (even without forms)
$loader->load('security.xml');
$loader->load('security_csrf.xml');

if ($container->getParameter('kernel.debug')) {
$loader->load('debug.xml');

Expand Down Expand Up @@ -158,9 +162,7 @@ private function registerFormConfiguration($config, ContainerBuilder $container,
if (!isset($config['session'])) {
throw new \LogicException('CSRF protection needs that sessions are enabled.');
}
if (!isset($config['secret'])) {
throw new \LogicException('CSRF protection needs a secret to be set.');
}

$loader->load('form_csrf.xml');

$container->setParameter('form.type_extension.csrf.enabled', true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,8 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="form.csrf_provider.class">Symfony\Component\Form\Extension\Csrf\CsrfProvider\SessionCsrfProvider</parameter>
</parameters>

<services>
<service id="form.csrf_provider" class="%form.csrf_provider.class%">
<argument type="service" id="session" />
<argument>%kernel.secret%</argument>
</service>
<service id="form.csrf_provider" class="Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfTokenGeneratorAdapter" parent="security.csrf.token_generator" />

<service id="form.type_extension.csrf" class="Symfony\Component\Form\Extension\Csrf\Type\FormTypeCsrfExtension">
<tag name="form.type_extension" alias="form" />
Expand Down
19 changes: 19 additions & 0 deletions src/Symfony/Bundle/FrameworkBundle/Resources/config/security.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="security.secure_random.class">Symfony\Component\Security\Core\Util\SecureRandom</parameter>
</parameters>

<services>
<!-- Pseudo-Random Number Generator -->
<service id="security.secure_random" class="%security.secure_random.class%">
<tag name="monolog.logger" channel="security" />
<argument>%kernel.cache_dir%/secure_random.seed</argument>
<argument type="service" id="logger" on-invalid="ignore" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<parameters>
<parameter key="security.csrf.token_generator.class">Symfony\Component\Security\Csrf\CsrfTokenGenerator</parameter>
<parameter key="security.csrf.token_storage.class">Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage</parameter>
</parameters>

<services>
<service id="security.csrf.token_storage" class="%security.csrf.token_storage.class%" public="false">
<argument type="service" id="session" />
</service>

<service id="security.csrf.token_generator" class="%security.csrf.token_generator.class%">
<argument type="service" id="security.csrf.token_storage" />
<argument type="service" id="security.secure_random" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public function block(FormView $view, $blockName, array $variables = array())
* Check the token in your action using the same intention.
*
* <code>
* $csrfProvider = $this->get('form.csrf_provider');
* $csrfProvider = $this->get('security.csrf.token_generator');
* if (!$csrfProvider->isCsrfTokenValid('rm_user_'.$user->getId(), $token)) {
* throw new \RuntimeException('CSRF attack detected.');
* }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ public function testCsrfProtection()
$this->assertEquals('%form.type_extension.csrf.enabled%', $def->getArgument(1));
$this->assertEquals('_csrf', $container->getParameter('form.type_extension.csrf.field_name'));
$this->assertEquals('%form.type_extension.csrf.field_name%', $def->getArgument(2));
$this->assertEquals('s3cr3t', $container->getParameterBag()->resolveValue($container->findDefinition('form.csrf_provider')->getArgument(1)));
}

public function testProxies()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected function getExtensions()
));

return array_merge(parent::getExtensions(), array(
new TemplatingExtension($this->engine, $this->csrfProvider, array(
new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
'FrameworkBundle:Form',
)),
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ protected function getExtensions()
));

return array_merge(parent::getExtensions(), array(
new TemplatingExtension($this->engine, $this->csrfProvider, array(
new TemplatingExtension($this->engine, $this->csrfTokenGenerator, array(
'FrameworkBundle:Form',
'FrameworkBundle:FormTable',
)),
Expand Down
4 changes: 3 additions & 1 deletion src/Symfony/Bundle/FrameworkBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@
"symfony/http-kernel": "~2.3",
"symfony/filesystem": "~2.3",
"symfony/routing": "~2.2",
"symfony/security-core": "~2.4",
"symfony/security-csrf": "~2.4",
"symfony/stopwatch": "~2.3",
"symfony/templating": "~2.1",
"symfony/translation": "~2.3",
"doctrine/common": "~2.2"
},
"require-dev": {
"symfony/finder": "~2.0",
"symfony/security": "~2.3",
"symfony/security": "~2.4",
"symfony/form": "~2.3",
"symfony/class-loader": "~2.1",
"symfony/validator": "~2.1"
Expand Down
7 changes: 4 additions & 3 deletions src/Symfony/Bundle/SecurityBundle/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
-----

* Added 'host' option to firewall configuration
* Moved 'security.secure_random' service configuration to FrameworkBundle

2.3.0
-----
Expand Down Expand Up @@ -79,9 +80,9 @@ CHANGELOG
logout:
path: /logout_path
target: /
csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token")
csrf_provider: form.csrf_provider # Required to enable protection
intention: logout # Optional (defaults to "logout")
csrf_parameter: _csrf_token # Optional (defaults to "_csrf_token")
csrf_provider: security.csrf.token_generator # Required to enable protection
intention: logout # Optional (defaults to "logout")
```

If the LogoutListener has CSRF protection enabled but cannot validate a token,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,5 @@
<argument type="service" id="security.context" />
<argument type="service" id="security.encoder_factory" />
</service>

<!-- Pseudorandom Number Generator -->
<service id="security.secure_random" class="Symfony\Component\Security\Core\Util\SecureRandom">
<tag name="monolog.logger" channel="security" />
<argument>%kernel.cache_dir%/secure_random.seed</argument>
<argument type="service" id="logger" on-invalid="ignore" />
</service>
</services>
</container>
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace Symfony\Bundle\SecurityBundle\Templating\Helper;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
use Symfony\Component\Templating\Helper\Helper;

/**
Expand Down Expand Up @@ -43,15 +43,15 @@ public function __construct(ContainerInterface $container, UrlGeneratorInterface
/**
* Registers a firewall's LogoutListener, allowing its URL to be generated.
*
* @param string $key The firewall key
* @param string $logoutPath The path that starts the logout process
* @param string $intention The intention for CSRF token generation
* @param string $csrfParameter The CSRF token parameter name
* @param CsrfProviderInterface $csrfProvider A CsrfProviderInterface instance
* @param string $key The firewall key
* @param string $logoutPath The path that starts the logout process
* @param string $csrfTokenId The ID of the CSRF token
* @param string $csrfParameter The CSRF token parameter name
* @param CsrfTokenGeneratorInterface $csrfTokenGenerator A CsrfTokenGeneratorInterface instance
*/
public function registerListener($key, $logoutPath, $intention, $csrfParameter, CsrfProviderInterface $csrfProvider = null)
public function registerListener($key, $logoutPath, $csrfTokenId, $csrfParameter, CsrfTokenGeneratorInterface $csrfTokenGenerator = null)
{
$this->listeners[$key] = array($logoutPath, $intention, $csrfParameter, $csrfProvider);
$this->listeners[$key] = array($logoutPath, $csrfTokenId, $csrfParameter, $csrfTokenGenerator);
}

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

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

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

if ('/' === $logoutPath[0]) {
$request = $this->container->get('request');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ security:
username_parameter: "user_login[username]"
password_parameter: "user_login[password]"
csrf_parameter: "user_login[_token]"
csrf_provider: form.csrf_provider
csrf_provider: security.csrf.token_generator
anonymous: ~
logout:
path: /logout_path
target: /
csrf_provider: form.csrf_provider
csrf_provider: security.csrf.token_generator

access_control:
- { path: .*, roles: IS_AUTHENTICATED_FULLY }
3 changes: 1 addition & 2 deletions src/Symfony/Bundle/SecurityBundle/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
],
"require": {
"php": ">=5.3.3",
"symfony/security": "~2.2",
"symfony/security": "~2.4",
"symfony/http-kernel": "~2.2"
},
"require-dev": {
"symfony/framework-bundle": "~2.2",
"symfony/twig-bundle": "~2.2",
"symfony/form": "~2.1",
"symfony/validator": "~2.2",
"symfony/yaml": "~2.0",
"symfony/expression-language": "~2.4"
Expand Down
8 changes: 7 additions & 1 deletion src/Symfony/Component/Form/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
CHANGELOG
=========

2.4.0
-----

* moved CSRF implementation to the new Security CSRF sub-component
* deprecated CsrfProviderInterface and its implementations
* deprecated options "csrf_provider" and "intention" in favor of the new options "csrf_token_generator" and "csrf_token_id"

2.3.0
------
-----

* deprecated FormPerformanceTestCase and FormIntegrationTestCase in the Symfony\Component\Form\Tests namespace and moved them to the Symfony\Component\Form\Test namespace
* deprecated TypeTestCase in the Symfony\Component\Form\Tests\Extension\Core\Type namespace and moved it to the Symfony\Component\Form\Test namespace
Expand Down
18 changes: 9 additions & 9 deletions src/Symfony/Component/Form/Extension/Csrf/CsrfExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
namespace Symfony\Component\Form\Extension\Csrf;

use Symfony\Component\Form\Extension\Csrf\Type;
use Symfony\Component\Form\Extension\Csrf\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\Form\AbstractExtension;
use Symfony\Component\Security\Csrf\CsrfTokenGeneratorInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
Expand All @@ -24,9 +24,9 @@
class CsrfExtension extends AbstractExtension
{
/**
* @var CsrfProviderInterface
* @var CsrfTokenGeneratorInterface
*/
private $csrfProvider;
private $tokenGenerator;

/**
* @var TranslatorInterface
Expand All @@ -41,13 +41,13 @@ class CsrfExtension extends AbstractExtension
/**
* Constructor.
*
* @param CsrfProviderInterface $csrfProvider The CSRF provider
* @param TranslatorInterface $translator The translator for translating error messages.
* @param null|string $translationDomain The translation domain for translating.
* @param CsrfTokenGeneratorInterface $tokenGenerator The CSRF token generator
* @param TranslatorInterface $translator The translator for translating error messages
* @param null|string $translationDomain The translation domain for translating
*/
public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInterface $translator = null, $translationDomain = null)
public function __construct(CsrfTokenGeneratorInterface $tokenGenerator, TranslatorInterface $translator = null, $translationDomain = null)
{
$this->csrfProvider = $csrfProvider;
$this->tokenGenerator = $tokenGenerator;
$this->translator = $translator;
$this->translationDomain = $translationDomain;
}
Expand All @@ -58,7 +58,7 @@ public function __construct(CsrfProviderInterface $csrfProvider, TranslatorInter
protected function loadTypeExtensions()
{
return array(
new Type\FormTypeCsrfExtension($this->csrfProvider, true, '_token', $this->translator, $this->translationDomain),
new Type\FormTypeCsrfExtension($this->tokenGenerator, true, '_token', $this->translator, $this->translationDomain),
);
}
}
Loading