diff --git a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php index eadd7e2912..7e25a122c6 100644 --- a/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php +++ b/src/Symfony/Component/Form/Extension/Csrf/CsrfProvider/DefaultCsrfProvider.php @@ -11,6 +11,8 @@ namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider; +use Symfony\Component\Security\Core\Util\StringUtils; + @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); /** @@ -61,7 +63,17 @@ class DefaultCsrfProvider implements CsrfProviderInterface */ public function isCsrfTokenValid($intention, $token) { - return $token === $this->generateCsrfToken($intention); + $expectedToken = $this->generateCsrfToken($intention); + + if (function_exists('hash_equals')) { + return hash_equals($expectedToken, $token); + } + + if (class_exists('Symfony\Component\Security\Core\Util\StringUtils')) { + return StringUtils::equals($expectedToken, $token); + } + + return $token === $expectedToken; } /** diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php index 15b71efd4d..f2048fdbe4 100644 --- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Security\Http\Firewall; use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Core\Util\StringUtils; use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint; use Psr\Log\LoggerInterface; use Symfony\Component\HttpKernel\Event\GetResponseEvent; @@ -99,7 +100,7 @@ class DigestAuthenticationListener implements ListenerInterface return; } - if ($serverDigestMd5 !== $digestAuth->getResponse()) { + if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) { if (null !== $this->logger) { $this->logger->debug('Unexpected response from the DigestAuth received; is the header returning a clear text passwords?', array('expected' => $serverDigestMd5, 'received' => $digestAuth->getResponse())); } diff --git a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php index ccadf94732..4186430708 100644 --- a/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/RememberMeListener.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Http\Event\InteractiveLoginEvent; use Symfony\Component\Security\Http\SecurityEvents; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface; +use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; /** * RememberMeListener implements authentication capabilities via a cookie. @@ -56,7 +57,7 @@ class RememberMeListener implements ListenerInterface $this->logger = $logger; $this->dispatcher = $dispatcher; $this->catchExceptions = $catchExceptions; - $this->sessionStrategy = $sessionStrategy; + $this->sessionStrategy = null === $sessionStrategy ? new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE) : $sessionStrategy; } /** @@ -77,7 +78,7 @@ class RememberMeListener implements ListenerInterface try { $token = $this->authenticationManager->authenticate($token); - if (null !== $this->sessionStrategy && $request->hasSession() && $request->getSession()->isStarted()) { + if ($request->hasSession() && $request->getSession()->isStarted()) { $this->sessionStrategy->onAuthentication($request, $token); } $this->tokenStorage->setToken($token); diff --git a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php index 4fb7e09c14..5f131cf8a5 100644 --- a/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php +++ b/src/Symfony/Component/Security/Http/RememberMe/PersistentTokenBasedRememberMeServices.php @@ -21,6 +21,7 @@ use Symfony\Component\Security\Core\Authentication\RememberMe\PersistentToken; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Util\SecureRandomInterface; use Psr\Log\LoggerInterface; +use Symfony\Component\Security\Core\Util\StringUtils; /** * Concrete implementation of the RememberMeServicesInterface which needs @@ -90,7 +91,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices list($series, $tokenValue) = $cookieParts; $persistentToken = $this->tokenProvider->loadTokenBySeries($series); - if ($persistentToken->getTokenValue() !== $tokenValue) { + if (!StringUtils::equals($persistentToken->getTokenValue(), $tokenValue)) { throw new CookieTheftException('This token was already used. The account is possibly compromised.'); } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php index b16d55b66b..7309042d47 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/RememberMeListenerTest.php @@ -246,6 +246,69 @@ class RememberMeListenerTest extends \PHPUnit_Framework_TestCase $listener->handle($event); } + public function testSessionIsMigratedByDefault() + { + list($listener, $tokenStorage, $service, $manager, , $dispatcher, $sessionStrategy) = $this->getListener(false, true, false); + + $tokenStorage + ->expects($this->once()) + ->method('getToken') + ->will($this->returnValue(null)) + ; + + $token = $this->getMock('Symfony\Component\Security\Core\Authentication\Token\TokenInterface'); + $service + ->expects($this->once()) + ->method('autoLogin') + ->will($this->returnValue($token)) + ; + + $tokenStorage + ->expects($this->once()) + ->method('setToken') + ->with($this->equalTo($token)) + ; + + $manager + ->expects($this->once()) + ->method('authenticate') + ->will($this->returnValue($token)) + ; + + $session = $this->getMock('\Symfony\Component\HttpFoundation\Session\SessionInterface'); + $session + ->expects($this->once()) + ->method('isStarted') + ->will($this->returnValue(true)) + ; + $session + ->expects($this->once()) + ->method('migrate') + ; + + $request = $this->getMock('\Symfony\Component\HttpFoundation\Request'); + $request + ->expects($this->any()) + ->method('hasSession') + ->will($this->returnValue(true)) + ; + + $request + ->expects($this->any()) + ->method('getSession') + ->will($this->returnValue($session)) + ; + + $event = $this->getGetResponseEvent(); + $event + ->expects($this->once()) + ->method('getRequest') + ->will($this->returnValue($request)) + ; + + $listener->handle($event); + } + public function testOnCoreSecurityInteractiveLoginEventIsDispatchedIfDispatcherIsPresent() { list($listener, $tokenStorage, $service, $manager, , $dispatcher) = $this->getListener(true);