[Form][FrameworkBundle][HttpFoundation] The session is now automatically started when a form is CSRF protected
This commit is contained in:
parent
e9a7531a26
commit
48af2fc86e
@ -35,9 +35,21 @@ class FrameworkBundle extends Bundle
|
||||
$this->container->get('error_handler');
|
||||
}
|
||||
if ($this->container->hasParameter('csrf_secret')) {
|
||||
FormConfiguration::setDefaultCsrfSecret($this->container->getParameter('csrf_secret'));
|
||||
FormConfiguration::addDefaultCsrfSecret($this->container->getParameter('csrf_secret'));
|
||||
FormConfiguration::enableDefaultCsrfProtection();
|
||||
}
|
||||
|
||||
$container = $this->container;
|
||||
|
||||
// the session ID should always be included in the CSRF token, even
|
||||
// if default CSRF protection is not enabled
|
||||
FormConfiguration::addDefaultCsrfSecret(function () use ($container) {
|
||||
// automatically starts the session when the CSRF token is
|
||||
// generated
|
||||
$container->get('session')->start();
|
||||
|
||||
return $container->get('session')->getId();
|
||||
});
|
||||
}
|
||||
|
||||
public function registerExtensions(ContainerBuilder $container)
|
||||
|
@ -161,11 +161,18 @@ class Form extends FieldGroup
|
||||
*/
|
||||
protected function generateCsrfToken($secret)
|
||||
{
|
||||
$sessId = session_id();
|
||||
if (!$sessId) {
|
||||
throw new \LogicException('The session must be started in order to generate a proper CSRF Token');
|
||||
$secret .= get_class($this);
|
||||
$defaultSecrets = FormConfiguration::getDefaultCsrfSecrets();
|
||||
|
||||
foreach ($defaultSecrets as $defaultSecret) {
|
||||
if ($defaultSecret instanceof \Closure) {
|
||||
$defaultSecret = $defaultSecret();
|
||||
}
|
||||
return md5($secret.$sessId.get_class($this));
|
||||
|
||||
$secret .= $defaultSecret;
|
||||
}
|
||||
|
||||
return md5($secret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,12 +194,8 @@ class Form extends FieldGroup
|
||||
}
|
||||
|
||||
if (null === $csrfSecret) {
|
||||
if (FormConfiguration::getDefaultCsrfSecret() !== null) {
|
||||
$csrfSecret = FormConfiguration::getDefaultCsrfSecret();
|
||||
} else {
|
||||
$csrfSecret = md5(__FILE__.php_uname());
|
||||
}
|
||||
}
|
||||
|
||||
$field = new HiddenField($csrfFieldName, array(
|
||||
'property_path' => null,
|
||||
|
@ -18,7 +18,7 @@ namespace Symfony\Component\Form;
|
||||
*/
|
||||
class FormConfiguration
|
||||
{
|
||||
protected static $defaultCsrfSecret = null;
|
||||
protected static $defaultCsrfSecrets = array();
|
||||
protected static $defaultCsrfProtection = false;
|
||||
protected static $defaultCsrfFieldName = '_token';
|
||||
|
||||
@ -89,22 +89,32 @@ class FormConfiguration
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the CSRF secret used in all new CSRF protected forms
|
||||
* Sets the default CSRF secrets to be used in all new CSRF protected forms
|
||||
*
|
||||
* @param string $secret
|
||||
* @param array $secrets
|
||||
*/
|
||||
static public function setDefaultCsrfSecret($secret)
|
||||
static public function setDefaultCsrfSecrets(array $secrets)
|
||||
{
|
||||
self::$defaultCsrfSecret = $secret;
|
||||
self::$defaultCsrfSecrets = $secrets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default CSRF secret
|
||||
* Adds CSRF secrets to be used in all new CSRF protected forms
|
||||
*
|
||||
* @param string $secret
|
||||
*/
|
||||
static public function addDefaultCsrfSecret($secret)
|
||||
{
|
||||
self::$defaultCsrfSecrets[] = $secret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default CSRF secrets
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
static public function getDefaultCsrfSecret()
|
||||
static public function getDefaultCsrfSecrets()
|
||||
{
|
||||
return self::$defaultCsrfSecret;
|
||||
return self::$defaultCsrfSecrets;
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +168,16 @@ class Session implements \Serializable
|
||||
$this->storage->regenerate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the session ID
|
||||
*
|
||||
* @return mixed The session ID
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->storage->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the locale
|
||||
*
|
||||
|
@ -44,6 +44,10 @@ class ArraySessionStorage implements SessionStorageInterface
|
||||
{
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
}
|
||||
|
||||
public function write($key, $data)
|
||||
{
|
||||
$this->data[$key] = $data;
|
||||
|
@ -83,6 +83,18 @@ class NativeSessionStorage implements SessionStorageInterface
|
||||
self::$sessionStarted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
if (!self::$sessionStarted) {
|
||||
throw new \RuntimeException('The session must be started before reading its ID');
|
||||
}
|
||||
|
||||
return session_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from this storage.
|
||||
*
|
||||
|
@ -23,6 +23,15 @@ interface SessionStorageInterface
|
||||
*/
|
||||
function start();
|
||||
|
||||
/**
|
||||
* Returns the session ID
|
||||
*
|
||||
* @return mixed The session ID
|
||||
*
|
||||
* @throws \RuntimeException If the session was not started yet
|
||||
*/
|
||||
function getId();
|
||||
|
||||
/**
|
||||
* Reads data from this storage.
|
||||
*
|
||||
|
@ -60,7 +60,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
||||
protected function setUp()
|
||||
{
|
||||
FormConfiguration::disableDefaultCsrfProtection();
|
||||
FormConfiguration::setDefaultCsrfSecret(null);
|
||||
FormConfiguration::setDefaultCsrfSecrets(array());
|
||||
$this->validator = $this->createMockValidator();
|
||||
$this->form = new Form('author', new Author(), $this->validator);
|
||||
}
|
||||
@ -111,13 +111,26 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertTrue(strlen($form->getCsrfSecret()) >= 32);
|
||||
}
|
||||
|
||||
public function testDefaultCsrfSecretCanBeSet()
|
||||
public function testDefaultCsrfSecretsCanBeAdded()
|
||||
{
|
||||
FormConfiguration::setDefaultCsrfSecret('foobar');
|
||||
$form = new Form('author', new Author(), $this->validator);
|
||||
$form->enableCsrfProtection();
|
||||
FormConfiguration::addDefaultCsrfSecret('foobar');
|
||||
|
||||
$this->assertEquals('foobar', $form->getCsrfSecret());
|
||||
$form = new Form('author', new Author(), $this->validator);
|
||||
$form->enableCsrfProtection('_token', 'secret');
|
||||
|
||||
$this->assertEquals(md5('secret'.get_class($form).'foobar'), $form['_token']->getData());
|
||||
}
|
||||
|
||||
public function testDefaultCsrfSecretsCanBeAddedAsClosures()
|
||||
{
|
||||
FormConfiguration::addDefaultCsrfSecret(function () {
|
||||
return 'foobar';
|
||||
});
|
||||
|
||||
$form = new Form('author', new Author(), $this->validator);
|
||||
$form->enableCsrfProtection('_token', 'secret');
|
||||
|
||||
$this->assertEquals(md5('secret'.get_class($form).'foobar'), $form['_token']->getData());
|
||||
}
|
||||
|
||||
public function testDefaultCsrfFieldNameCanBeSet()
|
||||
|
Reference in New Issue
Block a user