[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');
|
$this->container->get('error_handler');
|
||||||
}
|
}
|
||||||
if ($this->container->hasParameter('csrf_secret')) {
|
if ($this->container->hasParameter('csrf_secret')) {
|
||||||
FormConfiguration::setDefaultCsrfSecret($this->container->getParameter('csrf_secret'));
|
FormConfiguration::addDefaultCsrfSecret($this->container->getParameter('csrf_secret'));
|
||||||
FormConfiguration::enableDefaultCsrfProtection();
|
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)
|
public function registerExtensions(ContainerBuilder $container)
|
||||||
|
@ -161,11 +161,18 @@ class Form extends FieldGroup
|
|||||||
*/
|
*/
|
||||||
protected function generateCsrfToken($secret)
|
protected function generateCsrfToken($secret)
|
||||||
{
|
{
|
||||||
$sessId = session_id();
|
$secret .= get_class($this);
|
||||||
if (!$sessId) {
|
$defaultSecrets = FormConfiguration::getDefaultCsrfSecrets();
|
||||||
throw new \LogicException('The session must be started in order to generate a proper CSRF Token');
|
|
||||||
|
foreach ($defaultSecrets as $defaultSecret) {
|
||||||
|
if ($defaultSecret instanceof \Closure) {
|
||||||
|
$defaultSecret = $defaultSecret();
|
||||||
|
}
|
||||||
|
|
||||||
|
$secret .= $defaultSecret;
|
||||||
}
|
}
|
||||||
return md5($secret.$sessId.get_class($this));
|
|
||||||
|
return md5($secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,11 +194,7 @@ class Form extends FieldGroup
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (null === $csrfSecret) {
|
if (null === $csrfSecret) {
|
||||||
if (FormConfiguration::getDefaultCsrfSecret() !== null) {
|
$csrfSecret = md5(__FILE__.php_uname());
|
||||||
$csrfSecret = FormConfiguration::getDefaultCsrfSecret();
|
|
||||||
} else {
|
|
||||||
$csrfSecret = md5(__FILE__.php_uname());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$field = new HiddenField($csrfFieldName, array(
|
$field = new HiddenField($csrfFieldName, array(
|
||||||
|
@ -18,7 +18,7 @@ namespace Symfony\Component\Form;
|
|||||||
*/
|
*/
|
||||||
class FormConfiguration
|
class FormConfiguration
|
||||||
{
|
{
|
||||||
protected static $defaultCsrfSecret = null;
|
protected static $defaultCsrfSecrets = array();
|
||||||
protected static $defaultCsrfProtection = false;
|
protected static $defaultCsrfProtection = false;
|
||||||
protected static $defaultCsrfFieldName = '_token';
|
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
|
* @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();
|
$this->storage->regenerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the session ID
|
||||||
|
*
|
||||||
|
* @return mixed The session ID
|
||||||
|
*/
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
return $this->storage->getId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the locale
|
* Returns the locale
|
||||||
*
|
*
|
||||||
|
@ -44,6 +44,10 @@ class ArraySessionStorage implements SessionStorageInterface
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getId()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public function write($key, $data)
|
public function write($key, $data)
|
||||||
{
|
{
|
||||||
$this->data[$key] = $data;
|
$this->data[$key] = $data;
|
||||||
|
@ -83,6 +83,18 @@ class NativeSessionStorage implements SessionStorageInterface
|
|||||||
self::$sessionStarted = true;
|
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.
|
* Reads data from this storage.
|
||||||
*
|
*
|
||||||
|
@ -23,6 +23,15 @@ interface SessionStorageInterface
|
|||||||
*/
|
*/
|
||||||
function start();
|
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.
|
* Reads data from this storage.
|
||||||
*
|
*
|
||||||
|
@ -60,7 +60,7 @@ class FormTest extends \PHPUnit_Framework_TestCase
|
|||||||
protected function setUp()
|
protected function setUp()
|
||||||
{
|
{
|
||||||
FormConfiguration::disableDefaultCsrfProtection();
|
FormConfiguration::disableDefaultCsrfProtection();
|
||||||
FormConfiguration::setDefaultCsrfSecret(null);
|
FormConfiguration::setDefaultCsrfSecrets(array());
|
||||||
$this->validator = $this->createMockValidator();
|
$this->validator = $this->createMockValidator();
|
||||||
$this->form = new Form('author', new Author(), $this->validator);
|
$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);
|
$this->assertTrue(strlen($form->getCsrfSecret()) >= 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDefaultCsrfSecretCanBeSet()
|
public function testDefaultCsrfSecretsCanBeAdded()
|
||||||
{
|
{
|
||||||
FormConfiguration::setDefaultCsrfSecret('foobar');
|
FormConfiguration::addDefaultCsrfSecret('foobar');
|
||||||
$form = new Form('author', new Author(), $this->validator);
|
|
||||||
$form->enableCsrfProtection();
|
|
||||||
|
|
||||||
$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()
|
public function testDefaultCsrfFieldNameCanBeSet()
|
||||||
|
Reference in New Issue
Block a user