From d77a4c1fe29fa535c963881fa773b00124ae24a6 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 22 Mar 2015 22:27:57 -0400 Subject: [PATCH] Implement more time constant string comparisons. Prefer hash_equals if it exists. If not borrow the implementation from resonantcore/php-future. Refs #6139 --- src/Utility/Security.php | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) 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.