diff --git a/src/Utility/Security.php b/src/Utility/Security.php index f982bd02312..2d8d244203d 100644 --- a/src/Utility/Security.php +++ b/src/Utility/Security.php @@ -227,7 +227,7 @@ public static function decrypt($cipher, $key, $hmacSalt = null) $cipher = substr($cipher, $macSize); $compareHmac = hash_hmac('sha256', $cipher, $key); - if ($hmac !== $compareHmac) { + if (!static::_constantEquals($hmac, $compareHmac)) { return false; } @@ -235,6 +235,31 @@ public static function decrypt($cipher, $key, $hmacSalt = null) return $crypto->decrypt($cipher, $key); } + /** + * A timing attack resistant comparison that prefers native PHP implementations. + * + * @param string $hmac The hmac from the ciphertext being decrypted. + * @param string $compare The comparison hmac. + * @return bool + * @see https://github.com/resonantcore/php-future/ + */ + protected static function _constantEquals($hmac, $compare) + { + if (function_exists('hash_equals')) { + return hash_equals($hmac, $compare); + } + $hashLength = mb_strlen($hmac, '8bit'); + $compareLength = mb_strlen($compare, '8bit'); + if ($hashLength !== $compareLength) { + return false; + } + $result = 0; + for ($i = 0; $i < $hashLength; $i++) { + $result |= (ord($hmac[$i]) ^ ord($compare[$i])); + } + return $result === 0; + } + /** * Gets or sets the HMAC salt to be used for encryption/decryption * routines.