Хэш и соль для паролей в PHP: лучшие практики 2025 года

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Хэш, соль и ваша безопасность: почему это важно в 2025 году?

Представьте: вы написали крутой сайт, всё работает, пользователи регистрируются. Но однажды база данных утекает в сеть. Что увидят злоумышленники? Если вы храните пароли в открытом виде — это катастрофа. Если вы используете старый md5 — это почти то же самое. А если вы применили secure hash and salt — злоумышленники увидят лишь бессмысленный набор символов. В 2025 году это не просто рекомендация, а стандарт безопасности.

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

Почему md5 и sha1 — это зло?

Многие начинающие разработчики совершают одну и ту же ошибку: используют md5($password) или sha1($password). В 2025 году это не просто плохая практика, а прямой путь к уязвимости. Почему?

  • Скорость вычислений. MD5 и SHA1 созданы для скорости, а не для безопасности. Современные GPU позволяют перебирать миллиарды хэшей в секунду.
  • Отсутствие соли. Если два пользователя выбрали одинаковый пароль, их хэши будут идентичны. Это позволяет использовать радужные таблицы.
  • Коллизии. Для MD5 найдены практические коллизии — два разных входа дают одинаковый хэш.

К счастью, в PHP есть встроенные функции, которые решают все эти проблемы. И это не требует установки сторонних библиотек.

password_hash и password_verify: ваше секретное оружие

Начиная с PHP 5.5.0, в языке появились две волшебные функции: password_hash() и password_verify(). Они автоматически генерируют соль, используют криптостойкие алгоритмы и защищают от атак по времени.

Пример простейшего использования:

// Хэширование пароля
$password = "MySuperSecureP@ssw0rd!";
$hash = password_hash($password, PASSWORD_DEFAULT);
echo $hash; // $2y$10$...

Что здесь произошло? Функция password_hash:

  • Сгенерировала случайную соль
  • Выбрала оптимальный алгоритм (по умолчанию bcrypt)
  • Вычислила хэш
  • Вернула строку, которая содержит и хэш, и соль, и информацию об алгоритме

Проверка пароля выглядит так же просто:

// Проверка пароля
$inputPassword = "MySuperSecureP@ssw0rd!";
if (password_verify($inputPassword, $hash)) {
    echo "Пароль верный!";
} else {
    echo "Пароль неверный.";
}

Обратите внимание: вам не нужно хранить соль отдельно. Она уже встроена в хэш. Это и есть secure hash and salt в действии.

Какой алгоритм выбрать в 2025 году?

По умолчанию password_hash использует bcrypt. Это отличный выбор для большинства проектов. Но у нас есть и другие варианты:

Алгоритм Константа Рекомендация
Bcrypt PASSWORD_BCRYPT Отличный выбор, поддерживается везде
Argon2i PASSWORD_ARGON2I Устойчив к атакам по сторонним каналам
Argon2id PASSWORD_ARGON2ID Лучший выбор в 2025 году

Argon2id — победитель конкурса PHC (Password Hashing Competition). Он устойчив как к GPU-атакам, так и к атакам по времени. Если ваш PHP собран с поддержкой Argon2 (а в PHP 8.x это уже стандарт), используйте его.

Настройка стоимости хэширования

Одна из ключевых идей secure hash and salt — сделать вычисление хэша достаточно медленным, чтобы перебор был невозможен. В bcrypt это регулируется параметром cost:

$options = [
    'cost' => 12, // Чем выше, тем медленнее
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);

Для Argon2 параметры называются memory_cost, time_cost и threads:

$options = [
    'memory_cost' => 1 << 17, // 128 MB
    'time_cost'   => 4,
    'threads'     => 2,
];
$hash = password_hash($password, PASSWORD_ARGON2ID, $options);

Как выбрать правильные значения? Проведите тест на вашем сервере: хэширование должно занимать 200-400 миллисекунд. Этого достаточно, чтобы замедлить атакующего, но не раздражать пользователя.

Регистрация и авторизация: полный пример

Давайте соберем всё вместе. Вот как должен выглядеть процесс регистрации с secure hash and salt:

// registration.php
function registerUser($username, $password, $pdo) {
    // Проверяем, что пользователь не существует
    $stmt = $pdo->prepare("SELECT id FROM users WHERE username = ?");
    $stmt->execute([$username]);
    if ($stmt->fetch()) {
        return "Пользователь уже существует";
    }
    
    // Хэшируем пароль
    $hash = password_hash($password, PASSWORD_ARGON2ID, [
        'memory_cost' => 1 << 17,
        'time_cost'   => 4,
        'threads'     => 2,
    ]);
    
    // Сохраняем в базу
    $stmt = $pdo->prepare("INSERT INTO users (username, password_hash, created_at) VALUES (?, ?, NOW())");
    $stmt->execute([$username, $hash]);
    
    return "Регистрация успешна!";
}

А вот процесс авторизации:

// login.php
function loginUser($username, $password, $pdo) {
    // Получаем хэш из базы
    $stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $user = $stmt->fetch();
    
    if (!$user || !password_verify($password, $user['password_hash'])) {
        return "Неверное имя пользователя или пароль";
    }
    
    // Пароль верный, начинаем сессию
    $_SESSION['user_id'] = $user['id'];
    return "Добро пожаловать!";
}

Обратите внимание: никогда не сообщайте пользователю, что

Блоги

Рекомендаций по книгам