PHP 8.4: Стоимость Bcrypt по умолчанию изменена с 10 на 12

Источник: «PHP 8.4: Password Hashing: Default Bcrypt cost changed from 10 to 12»
В PHP 8.4 изменён параметр стоимости bcrypt по умолчанию встроенного в PHP API хеширования паролей.

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. Время указано в миллисекундах.

891011121314
Intel i5-2430M
2.4GHz - 2011
2141801613286571319
Apple M1 Pro
2021
153161121240480965
Intel Xeon E-2246G
3.60 GHz - 2019
11203978158316631
Intel Xeon E32145
3.30 GHz - 2011
1632641282565231047
AMD EPYC 7002
2019
142958116244485997
AMD Ryzen 4800H
2.9 GHz - 2020
214560105210416850
Intel Xeon Skylake
IBRS - 2020
132754108216432873
Бенчмарк длительности вычислений PHP BCrypt
Бенчмарк длительности вычислений PHP BCrypt

Влияние на обратную совместимость

В 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]);

Дополнительные материалы

Предыдущая Статья

PHP 8.4: Новые методы DateTime(Immutable)::get/setMicroseconds

Следующая Статья

PHP 8.4: Тип констант PHP_ZTS и PHP_DEBUG изменён с int на bool