security #16630 n/a (xabbuh)
This PR was merged into the 2.3 branch. Discussion ---------- n/a n/a Commits -------819aa54
prevent timing attacks in digest auth listener557ea17
mitigate CSRF timing attack vulnerabilityf1fd768
fix potential timing attack issue
This commit is contained in:
commit
c51977293a
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
|
namespace Symfony\Component\Form\Extension\Csrf\CsrfProvider;
|
||||||
|
|
||||||
|
use Symfony\Component\Security\Core\Util\StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default implementation of CsrfProviderInterface.
|
* Default implementation of CsrfProviderInterface.
|
||||||
*
|
*
|
||||||
|
@ -54,7 +56,17 @@ class DefaultCsrfProvider implements CsrfProviderInterface
|
||||||
*/
|
*/
|
||||||
public function isCsrfTokenValid($intention, $token)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Firewall;
|
||||||
|
|
||||||
use Symfony\Component\Security\Core\SecurityContextInterface;
|
use Symfony\Component\Security\Core\SecurityContextInterface;
|
||||||
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
use Symfony\Component\Security\Core\User\UserProviderInterface;
|
||||||
|
use Symfony\Component\Security\Core\Util\StringUtils;
|
||||||
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
|
use Symfony\Component\Security\Http\EntryPoint\DigestAuthenticationEntryPoint;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
|
@ -99,7 +100,7 @@ class DigestAuthenticationListener implements ListenerInterface
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($serverDigestMd5 !== $digestAuth->getResponse()) {
|
if (!StringUtils::equals($serverDigestMd5, $digestAuth->getResponse())) {
|
||||||
if (null !== $this->logger) {
|
if (null !== $this->logger) {
|
||||||
$this->logger->debug(sprintf('Expected response: "%s" but received: "%s"; is AuthenticationDao returning clear text passwords?', $serverDigestMd5, $digestAuth->getResponse()));
|
$this->logger->debug(sprintf('Expected response: "%s" but received: "%s"; is AuthenticationDao returning clear text passwords?', $serverDigestMd5, $digestAuth->getResponse()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
|
use Symfony\Component\Security\Core\Util\SecureRandomInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Security\Core\Util\StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete implementation of the RememberMeServicesInterface which needs
|
* Concrete implementation of the RememberMeServicesInterface which needs
|
||||||
|
@ -90,7 +91,7 @@ class PersistentTokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||||
list($series, $tokenValue) = $cookieParts;
|
list($series, $tokenValue) = $cookieParts;
|
||||||
$persistentToken = $this->tokenProvider->loadTokenBySeries($series);
|
$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.');
|
throw new CookieTheftException('This token was already used. The account is possibly compromised.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||||
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
use Symfony\Component\Security\Core\Exception\AuthenticationException;
|
||||||
use Symfony\Component\Security\Core\User\UserInterface;
|
use Symfony\Component\Security\Core\User\UserInterface;
|
||||||
|
use Symfony\Component\Security\Core\Util\StringUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Concrete implementation of the RememberMeServicesInterface providing
|
* Concrete implementation of the RememberMeServicesInterface providing
|
||||||
|
@ -53,7 +54,7 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||||
throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user)));
|
throw new \RuntimeException(sprintf('The UserProviderInterface implementation must return an instance of UserInterface, but returned "%s".', get_class($user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true !== $this->compareHashes($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) {
|
if (!StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) {
|
||||||
throw new AuthenticationException('The cookie\'s hash is invalid.');
|
throw new AuthenticationException('The cookie\'s hash is invalid.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,31 +65,6 @@ class TokenBasedRememberMeServices extends AbstractRememberMeServices
|
||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares two hashes using a constant-time algorithm to avoid (remote)
|
|
||||||
* timing attacks.
|
|
||||||
*
|
|
||||||
* This is the same implementation as used in the BasePasswordEncoder.
|
|
||||||
*
|
|
||||||
* @param string $hash1 The first hash
|
|
||||||
* @param string $hash2 The second hash
|
|
||||||
*
|
|
||||||
* @return bool true if the two hashes are the same, false otherwise
|
|
||||||
*/
|
|
||||||
private function compareHashes($hash1, $hash2)
|
|
||||||
{
|
|
||||||
if (strlen($hash1) !== $c = strlen($hash2)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result = 0;
|
|
||||||
for ($i = 0; $i < $c; ++$i) {
|
|
||||||
$result |= ord($hash1[$i]) ^ ord($hash2[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0 === $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
|
Reference in New Issue