Как хранить пароли в БД (PHP). Шифрование, хеширование паролей

Александр Кичатов

Почему нельзя хранить пароли в открытом виде

Главная причина - минимизация ущерба в случае утечки базы данных.

Если злоумышленник получит только логины и email-адреса пользователей - это плохо, но не критично.

Но если в его руках окажутся логины и пароли, он может попытаться использовать эти данные для входа в почтовые сервисы (Gmail, Яндекс.Почта, Mail.ru и т.д.), социальные сети, мессенджеры, клиент-банки и т.д.

В тот же личный кабинет Пятёрочки, чтобы перевыпустить карту и потратить чужие бонусы.

Мем с бабкой - Где мои баллы?!

В общем, пользователи сайта, которые везде используют одни и те же логины и пароли, могут получить кучу проблем.

Некоторые разработчики считают, что их приложение надёжно защищено и никаких утечек быть не может. Есть несколько причин, почему это мнение ошибочно:

  • Разработчик - не робот, он не может не совершать ошибок.
  • Взлом может произойти со стороны хостинг-провайдера, работу которого не всегда возможно контролировать.
  • Некорректная настройка сервера может привести к возможному доступу других пользователей хостинга к вашему сайту (актуально для виртуальных хостингов).
  • Бывший коллега по работе может слить базу данных конкурентам. Может в качестве мести, а может просто ради денег.

Короче, пароли в открытом виде хранить нельзя.

Шифрование и хеширование

Шифрование - это обратимое преобразование текста в случайный набор символов.

Хеширование - это необратимое преобразование текста в случайный набор символов.

Разница между этими двумя действиями в том, можем ли мы из случайного набора символов получить исходную строку по какому-то известному алгоритму.

Приведу пример шифрования. У нас есть сообщение:

Я Вася

Зашифруем сообщение по следующему алгоритму: сдвинем каждую букву на 1 в алфавитном порядке, т.е. а превращается в б, г превращается в д, я превращается в а. Так будет выглядеть зашифрованный текст:

А Гбта

Зашифровали. Теперь для расшифровки нужно выполнить обратную операцию, сдвинуть все буквы на 1 символ назад. К слову, этот алгоритм шифрования называется шифр Цезаря (Википедия).

В отличие от шифрования, хеширование не имеет (вернее, не должно иметь) способа "расхешировать" строку обратно:

$hash = md5('Какая-то строка');

Шифрование паролей

Не надо шифровать пароли.

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

Хеширование паролей и авторизация

Для хеширования паролей в PHP существует функция password_hash():

$password = '123456';
$hash = password_hash($password, PASSWORD_BCRYPT);

var_dump($hash);
// string(60) "$2y$10$Vb.pry5vRGNrm6Y79UfBsun/RbXq2.XEGCOMpozrDwg.MNpfxvWHK"

Вторым параметром передаётся алгоритм хеширования. По-умолчанию это указанный нами bcrypt, но я рекомендую указывать его вручную, поскольку базовый алгоритм в будущем может поменяться. Будет грустно, если при очередном обновлении версии PHP на сайте отвалится авторизация.

Для проверки корректности введённого пользователем пароля используется функция password_verify():

<?php
$hash = '$2y$10$Vb.pry5vRGNrm6Y79UfBsun/RbXq2.XEGCOMpozrDwg.MNpfxvWHK';
$password = '123456';

if(password_verify($password, $hash))
	echo 'Пароль правильный.';
else
	echo 'Пароль неправильный.';

Ещё раз. При регистрации пользователя нужно передать пароль в функцию password_hash(), а полученный хеш сохранить в базу данных.

При попытке авторизации получаем пользователя по его логину и проверяем функцией password_hash(), соответствует ли хеш пароля тому паролю, который ввёл пользователь.

Таким образом, хранить исходный пароль больше нет смысла.

Да, разные алгоритмы хеширования генерируют хеш разной длины, поэтому рекомендуется хранить хеш в поле с типом VARCHAR(255).

Алгоритмы MD5 и SHA1

В интернете ещё встречаются статьи, где рекомендуется хешировать пароли функциями md5() и sha1().

Для хеширования паролей их использовать нельзя!

Эти алгоритмы давно устарели и не являются безопасными (Документация). Вместо этого используйте функцию password_hash(), которую мы разобрали выше.

Комментарии