Как хешировать пароль: password_hash и password_verify
При регистрации сохраняйте в базу не пароль, а его хеш:
<?php
$hash = password_hash($password, PASSWORD_DEFAULT);При входе сверяйте введённый пароль с хешем из базы:
<?php
if (password_verify($password, $user['password_hash'])) {
// пароль верный
}Как это выглядит целиком:
<?php
$hash = password_hash('секретный пароль', PASSWORD_DEFAULT);
var_dump(password_verify('секретный пароль', $hash));
var_dump(password_verify('другой пароль', $hash));bool(true)
bool(false)Что такое хеш
Хеш — результат необратимого преобразования: из пароля хеш получить легко, из хеша пароль — нельзя. Если базу украдут, злоумышленник увидит хеши, а не пароли, и подбирать каждый пароль ему придётся долго.
Хеш от password_hash выглядит как строка вида $2y$… длиной 60 знаков (это алгоритм bcrypt). В нём уже есть всё нужное для проверки: алгоритм, «стоимость» вычисления и соль — случайная добавка, которую PHP создаёт сам. Из-за соли один и тот же пароль каждый раз даёт разный хеш, и сравнивать хеши напрямую бесполезно — только через password_verify.
Столбец для хеша в базе делайте VARCHAR(255): при смене алгоритма хеши могут стать длиннее 60 знаков.
Обновление хеша
PASSWORD_DEFAULT — алгоритм, который PHP считает лучшим сейчас; со временем он может измениться, а настройки стать строже. Хеши старых пользователей обновляют при входе — в момент, когда пароль известен:
<?php
if (password_verify($password, $user['password_hash'])) {
if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $pdo->prepare('UPDATE users SET password_hash = ? WHERE id = ?');
$stmt->execute([$newHash, $user['id']]);
}
// вход выполнен
}Если PHP собран с поддержкой Argon2 (в пакетах Ubuntu и Debian она есть), можно выбрать его явно: password_hash($password, PASSWORD_ARGON2ID). password_verify понимает хеши любого из этих алгоритмов.
Чего делать нельзя
Не храните пароли открытым текстом и не «шифруйте» их функциями md5, sha1, hash('sha256', …). Они считаются очень быстро: по украденной базе такие хеши подбирают миллиардами вариантов в секунду, а для простых паролей есть готовые таблицы ответов.
- Не придумывайте свою соль и свои схемы вроде
md5($password . 'соль')—password_hashделает это правильно. - Не сравнивайте хеши через
==и не ищите пользователя запросомWHERE password_hash = ?— сначала найдите пользователя по логину или почте, потом вызовитеpassword_verify. - Не ограничивайте пароль слишком коротким пределом длины. Учтите только, что bcrypt учитывает первые 72 байта пароля — это около 36 русских букв.
- Сообщение при неудачном входе одно на все случаи: «Неверный логин или пароль».
Пример входа с сессией — в уроке Сессии и куки, работа с базой — в уроке База данных через PDO.