Хеширование паролей в PHP: password_hash и password_verify для безопасной авторизации

Хеширование паролей в PHP: password_hash и password_verify для безопасной авторизации

Хеширование паролей в PHP: password_hash и password_verify для безопасной авторизации

Поисковый запрос, под который написана эта статья: PHP password_hash password_verify. Если вы делаете регистрацию, личный кабинет или админку, первое правило безопасности простое: никогда не храните пароль пользователя как обычный текст. В PHP для этого есть встроенные функции password_hash() и password_verify().

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

Допустим, пользователь ввёл пароль qwerty123, а вы записали его в базу данных без изменений. Если база утечёт, злоумышленник сразу получит доступ к аккаунтам. Плохо и другое решение — использовать md5 или sha1. Эти алгоритмы быстрые, устаревшие и легко перебираются по словарям.

Правильный подход — хранить не пароль, а его криптографический хеш, созданный специальным алгоритмом для паролей. В PHP это делается одной функцией:

<?php
$password = 'secret123';

$hash = password_hash($password, PASSWORD_DEFAULT);

echo $hash;

На выходе вы получите длинную строку. Именно её и нужно сохранять в базе данных вместо исходного пароля.

Что делает password_hash()

Функция password_hash() создаёт безопасный хеш пароля. Важный момент: она автоматически добавляет соль. Поэтому два одинаковых пароля почти всегда дадут разные хеши:

<?php
$password = 'secret123';

echo password_hash($password, PASSWORD_DEFAULT) . PHP_EOL;
echo password_hash($password, PASSWORD_DEFAULT) . PHP_EOL;

Новичков это иногда пугает: «Как тогда сравнивать пароль?» Сравнивать хеши вручную не нужно. Для этого есть password_verify().

Как проверить пароль через password_verify()

При входе пользователь вводит пароль. Вы берёте хеш из базы данных и передаёте оба значения в password_verify():

<?php
$passwordFromForm = 'secret123';
$hashFromDatabase = '$2y$10$exampleExampleExampleExampleExampleExampleExample';

if (password_verify($passwordFromForm, $hashFromDatabase)) {
    echo 'Пароль верный';
} else {
    echo 'Неверный пароль';
}

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

Пример регистрации пользователя

Ниже упрощённый пример обработки формы регистрации. Главное — перед сохранением пароля вызвать password_hash():

<?php
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';

if ($email === '' || $password === '') {
    exit('Заполните email и пароль');
}

if (strlen($password) < 8) {
    exit('Пароль должен быть не короче 8 символов');
}

$passwordHash = password_hash($password, PASSWORD_DEFAULT);

// Далее $passwordHash сохраняется в базу данных,
// например в поле password_hash.

echo 'Пользователь зарегистрирован';

Поле в базе данных лучше назвать не password, а password_hash. Так понятнее, что там хранится не исходный пароль.

Пример входа пользователя

При авторизации алгоритм такой: найти пользователя по email, получить его хеш пароля, проверить введённый пароль через password_verify().

<?php
$email = trim($_POST['email'] ?? '');
$password = $_POST['password'] ?? '';

// Допустим, эти данные пришли из базы данных.
$user = [
    'id' => 15,
    'email' => 'user@example.com',
    'password_hash' => password_hash('secret123', PASSWORD_DEFAULT),
];

if (!$user) {
    exit('Пользователь не найден');
}

if (!password_verify($password, $user['password_hash'])) {
    exit('Неверный email или пароль');
}

session_start();
$_SESSION['user_id'] = $user['id'];

echo 'Вы вошли';

Обратите внимание на сообщение об ошибке. В реальном проекте часто лучше писать одинаково: Неверный email или пароль. Так вы не подсказываете злоумышленнику, существует ли такой email.

PASSWORD_DEFAULT или PASSWORD_BCRYPT?

В большинстве случаев используйте PASSWORD_DEFAULT:

<?php
$hash = password_hash($password, PASSWORD_DEFAULT);

Это рекомендуемый вариант для обычных проектов. PHP сам выберет актуальный алгоритм по умолчанию. Сейчас часто используется bcrypt, но в будущем значение может измениться. Поэтому важно выделять в базе достаточно места под хеш: обычно используют VARCHAR(255).

Если вам нужно явно использовать bcrypt, можно написать так:

<?php
$hash = password_hash($password, PASSWORD_BCRYPT);

Настройка cost: когда это нужно

Для bcrypt можно задать параметр cost. Чем он выше, тем медленнее создаётся хеш. Это усложняет перебор паролей, но увеличивает нагрузку на сервер.

<?php
$hash = password_hash($password, PASSWORD_BCRYPT, [
    'cost' => 12,
]);

Не ставьте слишком большое значение «на всякий случай». Проверьте скорость на вашем сервере. Хороший ориентир: хеширование одного пароля должно занимать заметное, но приемлемое время, например десятки или сотни миллисекунд, а не несколько секунд.

Как обновлять старые хеши

PHP умеет проверять, не устарел ли хеш. Для этого есть password_needs_rehash(). Это полезно, если вы изменили алгоритм или параметры cost.

<?php
if (password_verify($password, $user['password_hash'])) {
    if (password_needs_rehash($user['password_hash'], PASSWORD_DEFAULT)) {
        $newHash = password_hash($password, PASSWORD_DEFAULT);

        // Обновите password_hash пользователя в базе данных.
    }

    echo 'Успешный вход';
}

Так можно постепенно обновлять безопасность без принудительного сброса паролей всем пользователям.

Типичные ошибки новичков

  • Хранить пароль в открытом виде — самая опасная ошибка.
  • Использовать md5 или sha1 — эти функции не подходят для паролей.
  • Сравнивать хеши вручную — используйте только password_verify().
  • Обрезать хеш в базе — используйте поле достаточной длины, например VARCHAR(255).
  • Отправлять пароль обратно пользователю — при восстановлении пароля нужно создавать ссылку или токен сброса, а не высылать старый пароль.
  • Минимальная структура таблицы

    Для старта таблица пользователей может выглядеть так:

    CREATE TABLE users (
        id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
        email VARCHAR(190) NOT NULL UNIQUE,
        password_hash VARCHAR(255) NOT NULL,
        created_at DATETIME NOT NULL
    );
    

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

    Практические рекомендации

  • Проверяйте минимальную длину пароля, например от 8 или 10 символов.
  • Не ограничивайте пароль слишком жёстко: пользователи должны иметь возможность использовать длинные фразы.
  • Всегда используйте HTTPS на сайте с авторизацией.
  • После входа сохраняйте в сессию ID пользователя, а не весь объект пользователя и не пароль.
  • Для запросов к базе используйте подготовленные выражения, чтобы не получить SQL-инъекцию.
  • Если вы хотите не просто выучить отдельные функции, а собрать полноценную регистрацию, авторизацию, работу с MySQL и защиту проекта на практике, посмотрите курс PHP и MySQL с нуля до уверенной разработки — он хорошо дополняет эту тему.

    Итог

    password_hash() и password_verify() — базовый минимум для безопасной работы с паролями в PHP. При регистрации создавайте хеш и сохраняйте его в базу. При входе проверяйте пароль через password_verify(). Не используйте md5, не храните пароли открытым текстом и заранее выделяйте достаточно места под хеш в таблице пользователей.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий