Skip to content

Commit dfd9218

Browse files
schmittjohfabpot
authored andcommitted
[Security/Http] Adds CSRF protection to the form-login
1 parent d22743c commit dfd9218

File tree

4 files changed

+59
-6
lines changed

4 files changed

+59
-6
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/FormLoginFactory.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory;
1313

14+
use Symfony\Component\DependencyInjection\Configuration\Builder\NodeBuilder;
1415
use Symfony\Component\DependencyInjection\DefinitionDecorator;
1516
use Symfony\Component\DependencyInjection\ContainerBuilder;
1617
use Symfony\Component\DependencyInjection\Reference;
@@ -27,6 +28,8 @@ public function __construct()
2728
{
2829
$this->addOption('username_parameter', '_username');
2930
$this->addOption('password_parameter', '_password');
31+
$this->addOption('csrf_parameter', '_csrf_token');
32+
$this->addOption('csrf_page_id', 'form_login');
3033
$this->addOption('post_only', true);
3134
}
3235

@@ -40,6 +43,15 @@ public function getKey()
4043
return 'form-login';
4144
}
4245

46+
public function addConfiguration(NodeBuilder $builder)
47+
{
48+
parent::addConfiguration($builder);
49+
50+
$builder
51+
->scalarNode('csrf_provider')->cannotBeEmpty()->end()
52+
;
53+
}
54+
4355
protected function getListenerId()
4456
{
4557
return 'security.authentication.listener.form';
@@ -57,6 +69,20 @@ protected function createAuthProvider(ContainerBuilder $container, $id, $config,
5769
return $provider;
5870
}
5971

72+
protected function createListener($container, $id, $config, $userProvider)
73+
{
74+
$listenerId = parent::createListener($container, $id, $config, $userProvider);
75+
76+
if (isset($config['csrf_provider'])) {
77+
$container
78+
->getDefinition($listenerId)
79+
->addArgument(new Reference($config['csrf_provider']))
80+
;
81+
}
82+
83+
return $listenerId;
84+
}
85+
6086
protected function createEntryPoint($container, $id, $config, $defaultEntryPoint)
6187
{
6288
$entryPointId = 'security.authentication.form_entry_point.'.$id;

src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,6 @@
116116
<service id="security.firewall.context" class="%security.firewall.context.class%" abstract="true">
117117
<argument type="collection" />
118118
<argument type="service" id="security.exception_listener" />
119-
</service>
119+
</service>
120120
</services>
121121
</container>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Symfony\Component\Security\Core\Exception;
4+
5+
/**
6+
* This exception is thrown when the csrf token is invalid.
7+
*
8+
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
9+
*/
10+
class InvalidCsrfTokenException extends AuthenticationException
11+
{
12+
}

src/Symfony/Component/Security/Http/Firewall/UsernamePasswordFormAuthenticationListener.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,16 @@
1111

1212
namespace Symfony\Component\Security\Http\Firewall;
1313

14+
use Symfony\Component\Form\CsrfProvider\CsrfProviderInterface;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpKernel\Log\LoggerInterface;
1417
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
1518
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
1619
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
17-
18-
use Symfony\Component\Security\Core\SecurityContextInterface;
1920
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
20-
use Symfony\Component\HttpKernel\Log\LoggerInterface;
21-
use Symfony\Component\HttpFoundation\Request;
2221
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
22+
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
23+
use Symfony\Component\Security\Core\SecurityContextInterface;
2324

2425
/**
2526
* UsernamePasswordFormAuthenticationListener is the default implementation of
@@ -29,16 +30,22 @@
2930
*/
3031
class UsernamePasswordFormAuthenticationListener extends AbstractAuthenticationListener
3132
{
33+
protected $csrfProvider;
34+
3235
/**
3336
* {@inheritdoc}
3437
*/
35-
public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, SessionAuthenticationStrategyInterface $sessionStrategy, $providerKey, array $options = array(), AuthenticationSuccessHandlerInterface $successHandler = null, AuthenticationFailureHandlerInterface $failureHandler = null, LoggerInterface $logger = null)
38+
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)
3639
{
3740
parent::__construct($securityContext, $authenticationManager, $sessionStrategy, $providerKey, array_merge(array(
3841
'username_parameter' => '_username',
3942
'password_parameter' => '_password',
43+
'csrf_parameter' => '_csrf_token',
44+
'csrf_page_id' => 'form_login',
4045
'post_only' => true,
4146
), $options), $successHandler, $failureHandler, $logger);
47+
48+
$this->csrfProvider = $csrfProvider;
4249
}
4350

4451
/**
@@ -54,6 +61,14 @@ protected function attemptAuthentication(Request $request)
5461
return null;
5562
}
5663

64+
if (null !== $this->csrfProvider) {
65+
$csrfToken = $request->get($this->options['csrf_parameter']);
66+
67+
if (false === $this->csrfProvider->isTokenValid($this->options['csrf_page_id'], $csrfToken)) {
68+
throw new InvalidCsrfTokenException('Invalid CSRF token.');
69+
}
70+
}
71+
5772
$username = trim($request->get($this->options['username_parameter']));
5873
$password = $request->get($this->options['password_parameter']);
5974

0 commit comments

Comments
 (0)