Чтобы предотвратить атаки по времени для сравнения хеш-строк, необходимо выполнить дополнительную подпись HMAC, чтобы рандомизировать процесс проверки (см. https://www.nccgroup.trust/us/about-us/newsroom-and-events/blog/2011/february/double-hmac-verification/).
В дополнение ко второму хешированию HMAC для каждого хеша, к обоим добавляется случайная соль произвольной длины, чтобы сделать время / процесс хеширования еще менее предсказуемым.
Моя реализация этого выглядит так:
function hmac_verify ($hash_original, $message, $key) {
$hmac_salt = '...'; // was added at the original HMAC signing
$random_salt = openssl_random_pseudo_bytes (rand(16,96));
$raw_hash = hash_hmac('sha512', $message . $hmac_salt, $key, true);
$hash_compare = base64_encode ($raw_hash); // $hash_original is in base64
$hash_compare_safe = hash_hmac('sha512', $hash_compare, $random_salt, true);
$hash_original_safe = hash_hmac('sha512', $hash_original, $random_salt, true);
if ($hash_compare_safe === $hash_original_safe) return true;
else return false;
}
Функция вызывается таким образом после расшифровки зашифрованного текста, чтобы проверить результат дешифрования:
if (!hmac_verify ($hmac_hash, $plaintext . $cipher_text, $key . $iv)) return "HASH ERROR";
Это успешно предотвратит атаку времени? Я делаю что-нибудь ненужное? Можно ли что-то улучшить?
Второй вопрос: целесообразнее ли выполнять проверку HMAC с открытым текстом, зашифрованным текстом или обоими (как в моем примере) и почему.
Я оставил несколько комментариев, пока я читал вашу функцию. Это не анализ после прочтения всего этого, скорее это то, о чем я сразу думаю, читая это.
function hmac_verify ($hash_original, $message, $key) {
##
# Nitpick: A variable named $hash_original will prime people who read
# your code to think of simple hash functions rather than HMAC
##
$hmac_salt = '...'; // was added at the original HMAC signing
##
# What is this? $hmac_salt? Looks like a hard coded-salt (a.k.a. pepper).
# I wouldn't trust this with my life.
##
$random_salt = openssl_random_pseudo_bytes (rand(16,96));
##
# Why are you bothering to randomize this? Just use a static value
# approximating the output size of the hash function (i.e. 64).
##
$raw_hash = hash_hmac('sha512', $message . $hmac_salt, $key, true);
$hash_compare = base64_encode ($raw_hash); // $hash_original is in base64
$hash_compare_safe = hash_hmac('sha512', $hash_compare, $random_salt, true);
##
# Ah, yeah, don't pepper. HMAC is secure.
##
$hash_original_safe = hash_hmac('sha512', $hash_original, $random_salt, true);
if ($hash_compare_safe === $hash_original_safe) return true;
else return false;
##
# Why not just do this?
# return $hash_compare_safe === $hash_original_safe;
##
}
Итак, я бы настоятельно рекомендовал разделить это на два отдельных механизма: один, который вычисляет MAC, и другой, который сравнивает строки в постоянное время (например, PHP 5.6 hash_equals()
делает).
function hmac_verify ($hmac, $message, $key)
{
$calc = hash_hmac('sha512', $message, $key, true);
return hmac_equals($hmac, $calc);
}
function hmac_equals($hmac, $calc)
{
$random = openssl_random_pseudo_bytes(64);
return (
hash_hmac('sha512', $hmac, $random)
===
hash_hmac('sha512', $calc, $random)
);
}
Других решений пока нет …