Commit 3639c56a authored by Fabien Potencier's avatar Fabien Potencier

added a CSRF service provider

parent b65d9315
......@@ -4,6 +4,7 @@ Changelog
2.0.0 (2015-XX-XX)
------------------
* [BC BREAK] CSRF has been moved to a standalone provider (``form.secret`` is not available anymore)
* added support for the Symfony HttpFoundation Twig bridge extension
* added support for the Symfony Asset Component
* bumped minimum version of Symfony to 2.7
......
......@@ -2,10 +2,11 @@ Disabling CSRF Protection on a Form using the FormExtension
===========================================================
The *FormExtension* provides a service for building form in your application
with the Symfony Form component. By default, the *FormExtension* uses the
CSRF Protection avoiding Cross-site request forgery, a method by which a
malicious user attempts to make your legitimate users unknowingly submit data
that they don't intend to submit.
with the Symfony Form component. When the `CSRF Service Provider
</providers/csrf.rst>` is registered, the *FormExtension* uses the CSRF
Protection avoiding Cross-site request forgery, a method by which a malicious
user attempts to make your legitimate users unknowingly submit data that they
don't intend to submit.
You can find more details about CSRF Protection and CSRF token in the
`Symfony Book
......
CsrfServiceProvider
===================
The *CsrfServiceProvider* provides a service for building forms in your
application with the Symfony2 Form component.
Parameters
----------
* none
Services
--------
* **form.csrf_provider**: An instance of an implementation of the
`CsrfProviderInterface
<http://api.symfony.com/master/Symfony/Component/Form/Extension/Csrf/CsrfProvider/CsrfProviderInterface.html>`_,
defaults to a `DefaultCsrfProvider
<http://api.symfony.com/master/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.html>`_.
Registering
-----------
.. code-block:: php
use Silex\Provider\CsrfServiceProvider;
$app->register(new CsrfServiceProvider());
.. note::
The *SerializerServiceProvider* relies on Symfony's `Serializer Component
<http://symfony.com/doc/current/components/serializer.html>`_, which comes
with the "fat" Silex archive but not with the regular one. If you are using
Composer, add it as a dependency:
.. code-block:: bash
composer require symfony/security-csrf
Usage
-----
When the CSRF Service Provider is registered, all forms created via the Form
Service Provider are protected against CSRF by default.
You can also use the CSRF protection even without using the Symfony Form
component. If, for example, you're doing a DELETE action, you can check the
CSRF token::
.. code-block:: php
use Symfony\Component\Security\Csrf\CsrfToken;
$app['csrf.token_manager']->isTokenValid(new CsrfToken('token_id', 'TOKEN'));
......@@ -7,10 +7,7 @@ your application with the Symfony Form component.
Parameters
----------
* **form.secret**: This secret value is used for generating and validating the
CSRF token for a specific page. It is very important for you to set this
value to a static randomly generated value, to prevent hijacking of your
forms. Defaults to ``md5(__DIR__)``.
* none
Services
--------
......@@ -19,9 +16,6 @@ Services
<http://api.symfony.com/master/Symfony/Component/Form/FormFactory.html>`_,
that is used to build a form.
* **form.csrf_provider**: An instance of an implementation of
`CsrfTokenManagerInterface <http://api.symfony.com/2.7/Symfony/Component/Security/Csrf/CsrfTokenManagerInterface.html>`_.
Registering
-----------
......@@ -64,13 +58,6 @@ Registering
composer require symfony/validator symfony/config symfony/translation
The Symfony Security CSRF component is used to protect forms against CSRF
attacks:
.. code-block:: bash
composer require symfony/security-csrf
If you want to use forms in your Twig templates, you can also install the
Symfony Twig Bridge. Make sure to install, if you didn't do that already,
the Translation component in order for the bridge to work:
......@@ -183,6 +170,11 @@ You can register form type guessers by extending ``form.type.guessers``::
return $guessers;
});
.. warning::
CSRF protection is only available and automatically enabled when the `CSRF
Service Provider </providers/csrf.rst>` is registered.
Traits
------
......
<?php
/*
* This file is part of the Silex framework.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Silex\Provider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
/**
* Symfony CSRF Security component Provider.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class CsrfServiceProvider implements ServiceProviderInterface
{
public function register(Container $app)
{
$app['csrf.token_manager'] = function ($app) {
return new CsrfTokenManager($app['csrf.token_generator'], $app['csrf.token_storage']);
};
$app['csrf.token_storage'] = function ($app) {
if (isset($app['session'])) {
return new SessionTokenStorage($app['session'], $app['csrf.session_namespace']);
}
return new NativeSessionTokenStorage($app['csrf.session_namespace']);
};
$app['csrf.token_generator'] = function ($app) {
return new UriSafeTokenGenerator();
};
$app['csrf.session_namespace'] = '_csrf';
}
}
......@@ -18,9 +18,6 @@ use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension as FormValidatorExtension;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\ResolvedFormTypeFactory;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
/**
* Symfony Form component Provider.
......@@ -46,8 +43,6 @@ class FormServiceProvider implements ServiceProviderInterface
require_once $path.'/NumberFormatter.php';
}
$app['form.secret'] = md5(__DIR__);
$app['form.types'] = function ($app) {
return array();
};
......@@ -62,18 +57,21 @@ class FormServiceProvider implements ServiceProviderInterface
$app['form.extension.csrf'] = function ($app) {
if (isset($app['translator'])) {
return new CsrfExtension($app['form.csrf_provider'], $app['translator']);
return new CsrfExtension($app['csrf.token_manager'], $app['translator']);
}
return new CsrfExtension($app['form.csrf_provider']);
return new CsrfExtension($app['csrf.token_manager']);
};
$app['form.extensions'] = function ($app) {
$extensions = array(
$app['form.extension.csrf'],
new HttpFoundationExtension(),
);
if (isset($app['csrf.token_manager'])) {
$extensions[] = $app['form.extension.csrf'];
}
if (isset($app['validator'])) {
$extensions[] = new FormValidatorExtension($app['validator']);
......@@ -103,11 +101,5 @@ class FormServiceProvider implements ServiceProviderInterface
$app['form.resolved_type_factory'] = function ($app) {
return new ResolvedFormTypeFactory();
};
$app['form.csrf_provider'] = function ($app) {
$storage = isset($app['session']) ? new SessionTokenStorage($app['session']) : new NativeSessionTokenStorage();
return new CsrfTokenManager(null, $storage);
};
}
}
......@@ -13,6 +13,8 @@ namespace Silex\Tests\Provider;
use Silex\Application;
use Silex\Provider\FormServiceProvider;
use Silex\Provider\CsrfServiceProvider;
use Silex\Provider\SessionServiceProvider;
use Silex\Provider\TranslationServiceProvider;
use Silex\Provider\ValidatorServiceProvider;
use Symfony\Component\Form\AbstractType;
......@@ -22,8 +24,6 @@ use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Translation\Exception\NotFoundResourceException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
class FormServiceProviderTest extends \PHPUnit_Framework_TestCase
{
......@@ -102,8 +102,8 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase
);
$app['locale'] = 'de';
$app['form.csrf_provider'] = function () {
return new FakeCsrfProvider();
$app['csrf.token_manager'] = function () {
return $this->getMock('Symfony\Component\Security\Csrf\CsrfTokenManagerInterface');
};
$form = $app['form.factory']->createBuilder('form', array())
......@@ -144,6 +144,19 @@ class FormServiceProviderTest extends \PHPUnit_Framework_TestCase
$this->fail('Form factory should not add a translation resource that does not exist');
}
}
public function testFormCsrf()
{
$app = new Application();
$app->register(new FormServiceProvider());
$app->register(new SessionServiceProvider());
$app->register(new CsrfServiceProvider());
$app['session.test'] = true;
$form = $app['form.factory']->createBuilder('form', array())->getForm();
$this->assertTrue(isset($form->createView()['_token']));
}
}
class DummyFormType extends AbstractType
......@@ -188,25 +201,3 @@ if (method_exists('Symfony\Component\Form\AbstractType', 'configureOptions')) {
}
}
}
class FakeCsrfProvider implements CsrfTokenManagerInterface
{
public function getToken($tokenId)
{
return new CsrfToken($tokenId, '123');
}
public function refreshToken($tokenId)
{
return new CsrfToken($tokenId, '123');
}
public function removeToken($tokenId)
{
}
public function isTokenValid(CsrfToken $token)
{
return '123' === $token->getValue();
}
}
......@@ -101,7 +101,6 @@ class ValidatorServiceProviderTest extends \PHPUnit_Framework_TestCase
$builder = $app['form.factory']->createBuilder('form', array(), array(
'constraints' => $constraints,
'csrf_protection' => false,
));
$form = $builder
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment