8000 Merge branch '2.3' into 2.7 · symfony/symfony@5edc71e · GitHub
[go: up one dir, main page]

Skip to content

Commit 5edc71e

Browse files
committed
Merge branch '2.3' into 2.7
* 2.3: migrate session after remember me authentication 8000 prevent timing attacks in digest auth listener mitigate CSRF timing attack vulnerability fix potential timing attack issue
2 parents d12cf19 + f95078c commit 5edc71e

File tree

5 files changed

+83
-5
lines changed

5 files changed

+83
-5
lines changed

src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
1313

14+
use Symfony\Component\Security\Core\Util\StringUtils;
15+
1416
@trigger_error('The '.__NAMESPACE__.'\DefaultCsrfProvider is deprecated since version 2.4 and will be removed in version 3.0. Use the \Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage class instead.', E_USER_DEPRECATED);
1517

1618
/**
@@ -61,7 +63,17 @@ public function generateCsrfToken($intention)
6163
*/
6264
public function isCsrfTokenValid($intention, $token)
6365
{
64-
return $token === $this->generateCsrfToken($intention);
66+
$expectedToken = $this->generateCsrfToken($intention);
67+
68+
if (function_exists('hash_equals')) {
69+
return hash_equals($expectedToken, $token);
70+
}
71+
72+
if (class_exists('Symfony\Component\Security\Core\Util\StringUtils')) {
73+
return StringUtils::equals($expectedToken, $token);
74+
}
75+
76+
return $token === $expectedToken;
6577
}
6678

6779
/**

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Security\Http\Firewall;
1313

1414
use Symfony\Component\Security\Core\User\UserProviderInterface;
15+
use Symfony\Component\Security\Core\Util\StringUtils;
1516
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
1617
use Psr\Log\LoggerInterface;
1718
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -99,7 +100,7 @@ public function handle(GetResponseEvent $event)
99100
return;
100101
}
101102

102-
if ($serverDigestMd5 !== $digestAuth->getResponse()) {
103+
if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) {
103104
if (null !== $this->logger) {
104105
$this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse()));
105106
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Security\Http\SecurityEvents;
2222
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2323
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface;
24+
use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy;
2425

2526
/**
2627
* RememberMeListener implements authentication capabilities via a cookie.
@@ -56,7 +57,7 @@ public function __construct(TokenStorageInterface $tokenStorage, RememberMeServi
5657
$this->logger = $logger;
5758
$this->dispatcher = $dispatcher;
5859
$this->catchExceptions = $catchExceptions;
59-
$this->sessionStrategy = $sessionStrategy;
60+
$this->sessionStrategy = null === $sessionStrategy ? new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE) : $sessionStrategy;
6061
}
6162

6263
/**
@@ -77,7 +78,7 @@ public function handle(GetResponseEvent $event)
7778

7879
try {
7980
$token = $this->authenticationManager->authenticate($token);
80-
if (null !== $this->sessionStrategy && $request->hasSession() && $request->getSession()->isStarted()) {
81+
if ($request->hasSession() && $request->getSession()->isStarted()) {
8182
$this->sessionStrategy->onAuthentication($request, $token);
8283
}
8384
$this->tokenStorage->setToken($token);

src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
2222
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
2323
use Psr\Log\LoggerInterface;
24+
use Symfony\Component\Security\Core\Util\StringUtils;
2425

2526
/**
2627
* Concrete implementation of the RememberMeServicesInterface which needs
@@ -90,7 +91,7 @@ protected function processAutoLoginCookie(array $cookieParts, Request $request)
9091
list($series, $tokenValue) = $cookieParts;
9192
$persistentToken = $this->tokenProvider->loadTokenBySeries($series);
9293

93-
if ($persistentToken->getTokenValue() !== $tokenValue) {
94+
if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) {
9495
throw new CookieTheftException('This token was already used. The account is possibly compromised.');
9596
}
9697

src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,69 @@ public function testSessionStrategy()
246246
$listener->handle($event);
247247
}
248248

249+
public function testSessionIsMigratedByDefault()
250+
{
251+
list($listener, $tokenStorage, $service, $manager, , $dispatcher, $sessionStrategy) = $this->getListener(false, true, false);
252+
253+
$tokenStorage
254+
->expects($this->once())
255+
->method('getToken')
256+
->will($this->returnValue(null))
257+
;
258+
259+
$token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface');
260+
$service
261+
->expects($this->once())
262+
->method('autoLogin')
263+
->will($this->returnValue($token))
264+
;
265+
266+
$tokenStorage
267+
->expects($this->once())
268+
->method('setToken')
269+
->with($this->equalTo($token))
270+
;
271+
272+
$manager
273+
->expects($this->once())
274+
->method('authenticate')
275+
->will($this->returnValue($token))
276+
;
277+
278+
$session = $this->getMock('\Symfony\Component\HttpFoundation\Session\SessionInterface');
279+
$session
280+
->expects($this->once())
281+
->method('isStarted')
282+
->will($this->returnValue(true))
283+
;
284+
$session
285+
->expects($this->once())
286+
->method('migrate')
287+
;
288+
289+
$request = $this->getMock('\Symfony\Component\HttpFoundation\Request');
290+
$request
291+
->expects($this->any())
292+
->method('hasSession')
293+
->will($this->returnValue(true))
294+
;
295+
296+
$request
297+
->expects($this->any())
298+
->method('getSession')
299+
->will($this->returnValue($session))
300+
;
301+
302+
$event = $this->getGetResponseEvent();
303+
$event
304+
->expects($this->once())
305+
->method('getRequest')
306+
->will($this->returnValue($request))
307+
;
308+
309+
$listener->handle($event);
310+
}
311+
249312
public function testOnCoreSecurityInteractiveLoginEventIsDispatchedIfDispatcherIsPresent()
250313
{
251314
list($listener, $tokenStorage, $service, $manager, , $dispatcher) = $this->getListener(true);

0 commit comments

Comments
 (0)
0