Итак, во-первых, давайте проясним одну вещь. openssl_digest()
=== hash()
. Это просто другая функция с другим именем, которая делает то же самое. Он вычисляет криптографический хэш ввода.
Итак, теперь у нас есть вопрос: При хранении паролей, что лучше: hash
или hash_hmac
?
Краткий ответ:
Ни
Длинный ответ:
Как выясняется, Радужный стол мертв . Просто использование hash($password . $salt)
или даже hash_hmac($password, $salt)
недостаточно для хранения пароля. Период. Если вы делаете это, остановитесь прямо сейчас.
Причина проста: время вычислений на компьютере (или графическом процессоре) невероятно дешево. Это настолько дешево, что просто перебор паролей настолько дешев, что вам нужно об этом беспокоиться. Помните, что хэш-функции рассчитаны на fast . Не дорого ...
Но, как оказалось, есть способ сделать эти быстрые хэш-функции более дорогими. На самом деле все довольно просто: итерация.
Теперь я знаю, о чем ты думаешь. Вы собираетесь просто перебрать хеш:
function hash_password($password, $salt) {
$hash = hash("sha512", $password . $salt);
for ($i = 0; $i < 1000; $i++) {
$hash = hash("sha512", $hash);
}
}
Конечно, это достаточно хорошо, верно? Нету. Как объяснено в Принципиальная разница между хешированием и шифрованием , это не очень хорошая идея. Так почему бы просто не вернуть пароль и снова ввести его?
function hash_password($password, $salt) {
$hash = hash("md5", $salt . $password);
for ($i = 0; $i < 1000; $i++) {
$hash = hash("md5", $hash . $password);
}
}
На самом деле, это именно то, что PHPASS использует (слегка подправлено, но это базовый алгоритм) ...
Итак, теперь 1 вызов hash_password
выполняет 1000 циклов хеширования.
Но можем ли мы улучшить это?
Ну, как оказалось, мы можем. Следующим логичным шагом было бы посмотреть, сможем ли мы получить больше циклов хеширования за то же время. И вот тут-то и приходит hash_hmac()
. Как оказалось, HMAC
использует 2 цикла хеширования при каждом вызове. И поскольку все это C, для выполнения одного раунда требуется всего лишь в 1,5 раза больше времени, чем hash()
.
Таким образом, это означает, что если мы заменим hash
на hash_hmac
, мы сразу увидим увеличение объема выполняемой работы на 33% за указанное время. Так что теперь мы здесь:
function hash_password($password, $salt) {
$hash = hash_hmac("md5", $salt, $password);
for ($i = 0; $i < 1000; $i++) {
$hash = hash_hmac("md5", $hash, $password);
}
}
И это на самом деле основной внутренний цикл PBKDF2 .
Но можем ли мы поправиться?
Да, опять, мы можем поправиться. Если мы посмотрим внимательнее, то увидим, что - помимо пароля и соли - все вышеперечисленные алгоритмы используют очень маленький объем памяти. В случае sha512 они будут использовать порядка от 128 до 256 байтов (буферов и состояния) для хэширования пароля. Поскольку использование памяти очень мало, тривиально запускать сразу несколько бок о бок в графическом процессоре тривиально. Если бы мы могли только увеличить использование памяти ...
Ну, как оказалось, мы можем просто использовать bcrypt
, который является алгоритмом адаптивного хеширования. Преимущество состоит в том, что он использует больше памяти, чем приведенные выше алгоритмы (порядка 4-5 КБ). Так что он более устойчив к распараллеливанию. И он устойчив к грубому принуждению, так как он вычислительно дорог.
К счастью, он доступен для PHP:
crypt($password, '$2y$07$usesomesillystringforsalt$')
Обратите внимание, что crypt()
использует много алгоритмов, но алгоритмы $2y$
и $2a$
bcrypt
.
Но можем ли мы улучшить это?
Вид-оф. Существует относительно новый алгоритм под названием scrypt . Это лучше, чем bcrypt, потому что это столь же дорого в вычислительном отношении, но использует ОЧЕНЬ больше памяти (порядка от 20 МБ до 40 МБ для хеширования одного пароля). Поэтому он еще более устойчив к распараллеливанию ...
К сожалению, scrypt
пока недоступен в PHP пока (я работаю над его изменением). До этого используйте bcrypt
...
Sidenote
После недавних уроков из LinkedIn , LastFM , Hotmail , Gawker и т. Д., Доказательство того, что многие людей делают это неправильно.Не делайте это неправильно, используйте библиотеку с проверенным алгоритмом.Используйте CRYPT_BLOWFISH
(bcrypt), используйте PHPASS, используйте PasswordLib .Но не придумывайте свои собственные только потому, что вы не хотите тянуть зависимость ... Это просто халатность.
Больше чтения: