diff options
-rw-r--r-- | Core/Util/StringUtils.php | 49 | ||||
-rw-r--r-- | Http/RememberMe/TokenBasedRememberMeServices.php | 2 |
2 files changed, 39 insertions, 12 deletions
diff --git a/Core/Util/StringUtils.php b/Core/Util/StringUtils.php index e8f3e3b..e68347f 100644 --- a/Core/Util/StringUtils.php +++ b/Core/Util/StringUtils.php @@ -38,29 +38,56 @@ class StringUtils */ public static function equals($knownString, $userInput) { - $knownString = (string) $knownString; - $userInput = (string) $userInput; + // Avoid making unnecessary duplications of secret data + if (!is_string($knownString)) { + $knownString = (string) $knownString; + } + + if (!is_string($userInput)) { + $userInput = (string) $userInput; + } if (function_exists('hash_equals')) { return hash_equals($knownString, $userInput); } - $knownLen = strlen($knownString); - $userLen = strlen($userInput); + $knownLen = self::safeStrlen($knownString); + $userLen = self::safeStrlen($userInput); - // Extend the known string to avoid uninitialized string offsets - $knownString .= $userInput; + if ($userLen !== $knownLen) { + return false; + } - // Set the result to the difference between the lengths - $result = $knownLen - $userLen; + $result = 0; - // Note that we ALWAYS iterate over the user-supplied length - // This is to mitigate leaking length information - for ($i = 0; $i < $userLen; $i++) { + for ($i = 0; $i < $knownLen; $i++) { $result |= (ord($knownString[$i]) ^ ord($userInput[$i])); } // They are only identical strings if $result is exactly 0... return 0 === $result; } + + /** + * Returns the number of bytes in a string. + * + * @param string $string The string whose length we wish to obtain + * + * @return int + */ + public static function safeStrlen($string) + { + // Premature optimization + // Since this cannot be changed at runtime, we can cache it + static $funcExists = null; + if (null === $funcExists) { + $funcExists = function_exists('mb_strlen'); + } + + if ($funcExists) { + return mb_strlen($string, '8bit'); + } + + return strlen($string); + } } diff --git a/Http/RememberMe/TokenBasedRememberMeServices.php b/Http/RememberMe/TokenBasedRememberMeServices.php index 9042963..3fe39ac 100644 --- a/Http/RememberMe/TokenBasedRememberMeServices.php +++ b/Http/RememberMe/TokenBasedRememberMeServices.php @@ -54,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))); } - if (true !== StringUtils::equals($hash, $this->generateCookieHash($class, $username, $expires, $user->getPassword()))) { + if (true !== StringUtils::equals($this->generateCookieHash($class, $username, $expires, $user->getPassword()), $hash)) { throw new AuthenticationException('The cookie\'s hash is invalid.'); } |