Хеширование паролей
Ради безопасности наших пользователей мы никогда не храним их пароли в читаемом виде, а храним только их отпечаток (так называемый хеш). Из хеша нельзя обратным путём получить исходный пароль. Важно использовать для создания хеша безопасный алгоритм. С этим нам помогает класс Nette\Security\Passwords.
Фреймворк автоматически добавляет в DI-контейнер сервис типа
Nette\Security\Passwords под именем security.passwords. Получить его можно,
попросив передать его через внедрение зависимостей.
use Nette\Security\Passwords;
class Foo
{
public function __construct(
private Passwords $passwords,
) {
}
}
__construct (string $algo=PASSWORD_DEFAULT, array $options=[])
Мы выбираем, какой безопасный алгоритм использовать для порождения хеша, и настраиваем его параметры.
По умолчанию используется PASSWORD_DEFAULT, то есть выбор алгоритма
оставлен на усмотрение PHP. В новых версиях PHP алгоритм может измениться,
если появятся более новые и стойкие алгоритмы хеширования. Поэтому вам
стоит учитывать, что длина получающегося хеша может измениться, и
хранить его так, чтобы вместилось достаточно символов; рекомендуемая
ширина – 255.
Пример задания скорости хеширования для алгоритма bcrypt изменением параметра cost: (в 2020 году по умолчанию 10, хеширование пароля занимает примерно 80 мс; при cost 11 – около 160 мс; при cost 12 – около 320 мс; чем медленнее, тем лучше защита, при скорости 10–12 защита уже считается достаточной)
// будем хешировать пароли 2^12 (2^cost) итерациями алгоритма bcrypt
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);
С использованием внедрения зависимостей:
services:
security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])
static bcrypt (?int $cost=null): Passwords
Создаёт экземпляр, настроенный на алгоритм bcrypt. Параметр $cost
задаёт описанную выше скорость хеширования; если вы его опустите,
будет использовано значение PHP по умолчанию.
$passwords = Passwords::bcrypt(12);
static argon2id (?int $memoryCost=null, ?int $timeCost=null, ?int $threads=null): Passwords
Создаёт экземпляр, настроенный на алгоритм Argon2id. Опущенные параметры
оставляются на значениях PHP по умолчанию. Если PHP собран без поддержки
Argon2, метод выбрасывает Nette\NotSupportedException.
$passwords = Passwords::argon2id(memoryCost: 1 << 17, timeCost: 4);
hash (string $password): string
Порождает хеш пароля.
$res = $passwords->hash($password); // Хеширует пароль
Результат $res – строка, которая кроме самого хеша содержит
идентификатор использованного алгоритма, его настройки и
криптографическую соль (случайные данные, обеспечивающие, что для
одного и того же пароля порождается разный хеш). Поэтому он обратно
совместим: например, если вы измените параметры, хеши, сохранённые с
прежними настройками, всё равно можно проверить. Весь этот результат
сохраняется в базу данных, так что хранить соль или настройки отдельно
не нужно.
verify (string $password, string $hash): bool
Выясняет, соответствует ли данный пароль данному хешу. $hash
возьмите из базы данных по введённому имени пользователя или адресу
электронной почты.
if ($passwords->verify($password, $hash)) {
// верный пароль
}
needsRehash (string $hash): bool
Выясняет, соответствует ли хеш параметрам, заданным в конструкторе.
Его удобно использовать, когда вы, например, меняете стоимость
хеширования. Проверка происходит по сохранённым настройкам, и если
needsRehash() вернёт true, нужно создать хеш заново, на этот раз с
новыми параметрами, и снова сохранить его в базу данных. Так
сохранённые хеши автоматически “обновляются” при входе
пользователей.
if ($passwords->needsRehash($hash)) {
$hash = $passwords->hash($password);
// сохраняем $hash в базу данных
}