PHP 8.4: Стоимость Bcrypt по умолчанию изменена с 10 на 12
PHP предоставляет функции password_hash
, password_verify
и password_needs_rehash
(с двумя дополнительными функциями для получения списка поддерживаемых алгоритмов и для получения информации из хэша) для безопасного хэширования паролей. Алгоритм хеширования пароля и его параметры настраиваются, а алгоритм и параметры хранятся в самом хеше пароля, поэтому пароль можно проверить, используя те же параметры, на основе которых был вычислен хеш.
Когда API хеширования паролей был представлен в PHP 5.5 (2013), он поддерживал Bcrypt в качестве одного из алгоритмов, на что указывала константа PASSWORD_BCRYPT
.
password_hash('hunter2', PASSWORD_BCRYPT);
// "$2y$10$GUYJOA4aWc6HegA2x.85PeZ9yjP2HCKFjb44C3vz1jlpUNb4AgYz2"
PHP 5.5 также поддерживал PASSWORD_DEFAULT
, разработанный для изменения со временем. Алгоритм по умолчанию не менялся, и в будущем его изменение также не планируется.
Также можно изменить параметры алгоритма хеширования:
password_hash('hunter2', PASSWORD_BCRYPT, ['cost' => 10]);
// "$2y$10$dI8qa2bKk/RN2hemdHkOgu8oICMn7ivhzVyJmiJmNFzjJpYsLEAYK"
Для PASSWORD_BCRYPT
/PASSWORD_DEFAULT
значение cost
по умолчанию равно 10
. Параметр cost
— это количество используемых итераций, вводимое как степень 2. Например, значение cost
, равное 10
, означает 2**10
итераций.
В PHP 8.4 параметр cost
алгоритма PASSWORD_BCRYPT
/PASSWORD_DEFAULT
был изменён с 10
на 12
, чтобы сделать пароли более устойчивыми и сложными для вычисления, учитывая более мощное аппаратное обеспечение по сравнению с тем, когда было принято значение по умолчанию.
Это изменение по сути равнозначно:
-password_hash('hunter2', PASSWORD_BCRYPT, ['cost' => 10]);
+password_hash('hunter2', PASSWORD_BCRYPT, ['cost' => 12]);
Показатели стоимости вычислений
В RFC, предлагаемыми изменениями, приводились результаты тестирования на различном доступном в настоящее время оборудовании, в которых сравнивалось время, необходимое для вычисления хэша пароля с затратами 10
, 11
и 12
.
Правильный баланс этой стоимости заключается в том, что она должна быть достаточно быстрой для вычисления сервером, чтобы не мешать работе пользователя, но достаточно медленной, чтобы её было сложно вычислить методом перебора в случае, если злоумышленник получит хэши паролей.
Следующие таблица и график составлены на основе данных, приведённых в RFC, а также дополнительных бенчмарков, проведённых PHP.Watch. Для каждого процессора среднее время вычисления хэша при различных параметрах cost
. Время указано в миллисекундах.
8 | 9 | 10 | 11 | 12 | 13 | 14 | |
---|---|---|---|---|---|---|---|
Intel i5-2430M 2.4GHz - 2011 | 21 | 41 | 80 | 161 | 328 | 657 | 1319 |
Apple M1 Pro 2021 | 15 | 31 | 61 | 121 | 240 | 480 | 965 |
Intel Xeon E-2246G 3.60 GHz - 2019 | 11 | 20 | 39 | 78 | 158 | 316 | 631 |
Intel Xeon E32145 3.30 GHz - 2011 | 16 | 32 | 64 | 128 | 256 | 523 | 1047 |
AMD EPYC 7002 2019 | 14 | 29 | 58 | 116 | 244 | 485 | 997 |
AMD Ryzen 4800H 2.9 GHz - 2020 | 21 | 45 | 60 | 105 | 210 | 416 | 850 |
Intel Xeon Skylake IBRS - 2020 | 13 | 27 | 54 | 108 | 216 | 432 | 873 |
Влияние на обратную совместимость
В PHP 8.4 параметр cost
по умолчанию в PASSWORD_BCRYPT
/PASSWORD_DEFAULT
изменён на 12
. В приложениях явно задающих стоимость всё останется без изменений.
Параметры для PASSWORD_ARGON2I
или PASSWORD_ARGON2ID
остаются неизменными, и в приложениях использующих их, всё останется без изменений.
Приложения, использующие параметры cost
по умолчанию, получат несколько большее время вычисления пароля, но это ожидаемый результат — увеличение времени вычисления для злоумышленников, пытающихся перебрать хэши паролей.
Автоматическое повторное хеширование
Когда в PHP появился API хэширования паролей, было рекомендовано вызывать функцию password_needs_rehash
после успешного вызова password_verify
, чтобы проверить, нужно ли повторно хэшировать пароль.
Ниже приведён пример использования PASSWORD_BCRYPT
, проверяющий необходимость повторного хэширования пароля. Поскольку параметр cost
изменился в PHP 8.4, password_needs_rehash
возвращает true
при значениях по умолчанию. Это одноразовое повторное хэширование, если обновлённый хэш сохраняется в базе данных и используется в дальнейшем.
// Когда пользователь вошёл в систему:
if (password_verify($password, $hash)) {
// Пароль корректный.
// Проверка, нуждается ли пароль в повторном хешировании:
if (password_needs_rehash($hash, PASSWORD_BCRYPT)) {
// Если это так, создаём новый хэш и заменяем старый
$newHash = password_hash($password, PASSWORD_BCRYPT);
// Обновление записи user с $newHash
// ...
}
// Продолжение процесса логина.
}
Как вернуть старый параметр стоимости
Если на серверах установлены очень старые или маломощные процессоры, или если приложению необходимо работать с предыдущим значением cost
по умолчанию 10
, можно вернуться к старому времени вычисления хэша, явно задав параметр cost
.
-password_hash('hunter2', PASSWORD_BCRYPT);
+password_hash('hunter2', PASSWORD_BCRYPT, ['cost' => 10]);
-password_hash('hunter2', PASSWORD_DEFAULT);
+password_hash('hunter2', PASSWORD_BCRYPT, ['cost' => 10]);