Хеширование паролей

Для обеспечения безопасности наших пользователей мы никогда не храним их пароли в открытом виде, вместо этого мы храним хэш пароля. Хеширование не является обратимой операцией, пароль не может быть восстановлен. Однако пароль можно взломать, и чтобы сделать взлом как можно более трудным, мы должны использовать безопасный алгоритм. Класс Nette\Security\Passwords поможет нам в этом.

Установка и требования

Фреймворк автоматически добавляет сервис Nette\Security\Passwords в контейнер DI под именем security.passwords, который вы получаете, передавая его с помощью инъекции зависимостей:

use Nette\Security\Passwords;

class Foo
{
	public function __construct(
		private Passwords $passwords,
	) {
	}
}

__construct($algo=PASSWORD_DEFAULT, array $options=[])string

Выбирает, какой безопасный алгоритм используется для хэширования и как его настроить.

По умолчанию используется PASSWORD_DEFAULT, поэтому выбор алгоритма остается за PHP. Алгоритм может измениться в новых релизах PHP, когда будут поддерживаться более новые и сильные алгоритмы хэширования. Поэтому вы должны знать, что длина результирующего хэша может измениться. Поэтому вы должны хранить результирующий хэш таким образом, чтобы он мог хранить достаточное количество символов, рекомендуемая ширина 255.

Вот как можно использовать алгоритм bcrypt и изменить скорость хэширования с помощью параметра cost от значения по умолчанию 10. В 2020 году при стоимости 10 хэширование одного пароля занимает примерно 80 мс, при стоимости 11 – 160 мс, при стоимости 12 – 320 мс, шкала логарифмическая. Чем медленнее, тем лучше, стоимость 10–12 считается достаточно медленной для большинства случаев использования:

// we will hash passwords with 2^12 (2^cost) iterations of the bcrypt algorithm
$passwords = new Passwords(PASSWORD_BCRYPT, ['cost' => 12]);

С инъекцией зависимостей:

services:
	security.passwords: Nette\Security\Passwords(::PASSWORD_BCRYPT, [cost: 12])

hash(string $passwords): string

Генерирует хэш пароля.

$res = $passwords->hash($password); // Hashes the password

Результат $res представляет собой строку, которая, помимо самого хэша, содержит идентификатор используемого алгоритма, его настройки и криптографическую соль (случайные данные, гарантирующие, что для одного и того же пароля будет сгенерирован другой хэш). Таким образом, обеспечивается обратная совместимость, например, при изменении параметров можно проверить хэши, сохраненные с использованием предыдущих настроек. Весь результат сохраняется в базе данных, поэтому нет необходимости хранить соль или настройки отдельно.

verify(string $password, string $hash)bool

Выясняет, совпадает ли заданный пароль с заданным хэшем. Получить $hash из базы данных по имени пользователя или адресу электронной почты.

if ($passwords->verify($password, $hash)) {
	// Correct password
}

needsRehash(string $hash): bool

Выясняет, соответствует ли хэш параметрам, заданным в конструкторе.

Используйте этот метод, когда вы, например, изменяете параметры хэша. Проверка пароля будет использовать параметры, сохраненные вместе с хэшем, и если needsRehash() вернет true, вам придется снова вычислить хэш, на этот раз с обновленными параметрами, и снова сохранить его в базе данных. Это гарантирует, что хэши паролей будут автоматически “обновляться” при входе пользователей в систему.

if ($passwords->needsRehash($hash)) {
	$hash = $passwords->hash($password);
	// store $hash to database
}