[Security/Http] Adds CSRF protection to the form-login

This commit is contained in:
Johannes Schmitt 2011-02-15 21:17:36 +01:00 committed by Fabien Potencier
parent d22743cf3a
commit dfd921822a
4 changed files with 59 additions and 6 deletions

View File

@ -11,6 +11,7 @@
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory; namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder;
use Symfony\Component\DependencyInjection\DefinitionDecorator; use Symfony\Component\DependencyInjection\DefinitionDecorator;
use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\DependencyInjection\Reference;
@ -27,6 +28,8 @@ class FormLoginFactory extends AbstractFactory
{ {
$this->addOption('username_parameter', '_username'); $this->addOption('username_parameter', '_username');
$this->addOption('password_parameter', '_password'); $this->addOption('password_parameter', '_password');
$this->addOption('csrf_parameter', '_csrf_token');
$this->addOption('csrf_page_id', 'form_login');
$this->addOption('post_only', true); $this->addOption('post_only', true);
} }
@ -40,6 +43,15 @@ class FormLoginFactory extends AbstractFactory
return 'form-login'; return 'form-login';
} }
public function addConfiguration(NodeBuilder $builder)
{
parent::addConfiguration($builder);
$builder
->scalarNode('csrf_provider')->cannotBeEmpty()->end()
;
}
protected function getListenerId() protected function getListenerId()
{ {
return 'security.authentication.listener.form'; return 'security.authentication.listener.form';
@ -57,6 +69,20 @@ class FormLoginFactory extends AbstractFactory
return $provider; return $provider;
} }
protected function createListener($container, $id, $config, $userProvider)
{
$listenerId = parent::createListener($container, $id, $config, $userProvider);
if (isset($config['csrf_provider'])) {
$container
->getDefinition($listenerId)
->addArgument(new Reference($config['csrf_provider']))
;
}
return $listenerId;
}
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint) protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
{ {
$entryPointId = 'security.authentication.form_entry_point.'.$id; $entryPointId = 'security.authentication.form_entry_point.'.$id;

View File

@ -0,0 +1,12 @@
<?php
namespace Symfony\Component\Security\Core\Exception;
/**
* This exception is thrown when the csrf token is invalid.
*
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
*/
class InvalidCsrfTokenException extends AuthenticationException
{
}

View File

@ -11,15 +11,16 @@
namespace Symfony\Component\Security\Http\Firewall; namespace Symfony\Component\Security\Http\Firewall;
use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface; use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\HttpKernel\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\SecurityContextInterface;
/** /**
* UsernamePasswordFormAuthenticationListener is the default implementation of * UsernamePasswordFormAuthenticationListener is the default implementation of
@ -29,16 +30,22 @@ use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
*/ */
class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener
{ {
protected $csrfProvider;
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null) public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null, CsrfProviderInterface $csrfProvider = null)
{ {
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array( parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array(
'username_parameter' => '_username', 'username_parameter' => '_username',
'password_parameter' => '_password', 'password_parameter' => '_password',
'csrf_parameter' => '_csrf_token',
'csrf_page_id' => 'form_login',
'post_only' => true, 'post_only' => true,
), $options), $successHandler, $failureHandler, $logger); ), $options), $successHandler, $failureHandler, $logger);
$this->csrfProvider = $csrfProvider;
} }
/** /**
@ -54,6 +61,14 @@ class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationL
return null; return null;
} }
if (null !== $this->csrfProvider) {
$csrfToken = $request->get($this->options['csrf_parameter']);
if (false === $this->csrfProvider->isTokenValid($this->options['csrf_page_id'], $csrfToken)) {
throw new InvalidCsrfTokenException('Invalid CSRF token.');
}
}
$username = trim($request->get($this->options['username_parameter'])); $username = trim($request->get($this->options['username_parameter']));
$password = $request->get($this->options['password_parameter']); $password = $request->get($this->options['password_parameter']);