Avatar ·

Безопасное хеширование и соль для паролей в PHP

Всем привет! Я начинающий PHP-разработчик и пытаюсь разобраться с безопасным хранением паролей пользователей. Сейчас я использую MD5 с солью, но мне сказали, что это небезопасно. Мой текущий код выглядит так: ```php function createPassword($password) { $salt = 'my_very_secret_salt_12345'; return md5($salt . $password); } function checkPassword($password, $hash) { $salt = 'my_very_secret_salt_12345'; return md5($salt . $password) === $hash; } ``` Я понимаю, что MD5 устарел, но не знаю, чем его заменить. Слышал про bcrypt, password_hash и password_verify, но не понимаю, как их правильно использовать. Основные вопросы: 1. Как правильно генерировать соль для каждого пароля? 2. Нужно ли хранить соль отдельно и как её передавать при проверке? 3. Какие алгоритмы сейчас считаются безопасными в PHP? 4. Как защититься от атак перебором (brute force) и радужных таблиц? Буду благодарен за подробные примеры кода с объяснениями. Хочу понять не просто "как сделать", а "почему это безопасно". Заранее спасибо! <<>>

Правильное решение: используйте встроенные функции PHP

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

Основные функции

  • password_hash() — создаёт хеш пароля с автоматической генерацией соли
  • password_verify() — проверяет пароль против хеша
  • password_needs_rehash() — проверяет, нужно ли обновить хеш (полезно при смене алгоритма)

Пример правильной реализации


<?php
// Регистрация пользователя
function registerUser($password) {
    // PASSWORD_BCRYPT использует алгоритм bcrypt (по умолчанию)
    // 'cost' — параметр, влияющий на вычислительную сложность (по умолчанию 10)
    $hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
    
    // Сохраняем $hash в базу данных (всё вместе: алгоритм, соль, хеш)
    return $hash;
}

// Авторизация пользователя
function loginUser($password, $hashFromDatabase) {
    // password_verify извлекает соль из хеша и проверяет пароль
    if (password_verify($password, $hashFromDatabase)) {
        return "Пароль верный!";
    } else {
        return "Неверный пароль!";
    }
}

// Пример использования
$hash = registerUser('my_secure_password');
echo $hash; // Вывод: $2y$12$... (длинная строка, содержащая всё необходимое)

$result = loginUser('my_secure_password', $hash);
echo $result; // "Пароль верный!"
?>

Почему это безопасно?

  • Автоматическая соль: Каждый хеш содержит уникальную, криптостойкую соль (22 символа base64). Соль хранится прямо в строке хеша, вам не нужно хранить её отдельно.
  • bcrypt: Использует алгоритм Blowfish, который специально разработан медленным для защиты от перебора. Даже с мощным GPU перебор bcrypt-хешей крайне неэффективен.
  • Радужные таблицы: Бесполезны, так как соль уникальна для каждого пароля.
  • Параметр cost: Увеличивает время вычисления хеша. Значение 12 делает его примерно в 4 раза медленнее, чем значение по умолчанию 10. Это замедляет атаки перебором.

Что ещё нужно знать?

Если вы обновляете PHP или меняете алгоритм (например, на Argon2), используйте password_needs_rehash():


<?php
// При каждом входе пользователя проверяем, нужно ли обновить хеш
if (password_needs_rehash($hashFromDatabase, PASSWORD_BCRYPT, ['cost' => 12])) {
    $newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
    // Сохраняем $newHash в базу данных
}
?>

Важно: Никогда не используйте md5(), sha1() или sha256() для паролей. Они слишком быстрые и уязвимы к перебору. Только password_hash() с bcrypt или Argon2 (начиная с PHP 7.2).

<<>>

Альтернатива: Argon2 (рекомендуется для PHP 7.2+)

Если ваш сервер использует PHP 7.2 или новее, рассмотрите алгоритм Argon2. Он был признан победителем конкурса Password Hashing Competition и считается более безопасным, чем bcrypt. Использование почти идентично:


<?php
// Argon2i — защищён от side-channel атак
$hash = password_hash($password, PASSWORD_ARGON2I, [
    'memory_cost' => 1024, // 1 MB памяти
    'time_cost' => 2,      // 2 итерации
    'threads' => 2         // 2 потока (только для Argon2id)
]);

// Argon2id — комбинирует защиту от side-channel и GPU (рекомендуется)
$hash = password_hash($password, PASSWORD_ARGON2ID, [
    'memory_cost' => 2048, // 2 MB
    'time_cost' => 3,
    'threads' => 2
]);

// Проверка остаётся такой же
if (password_verify($password, $hash)) {
    echo "Пароль верный!";
}
?>

Преимущества Argon2 перед bcrypt:

  • Устойчив к атакам на GPU (нужна большая память, что снижает параллелизацию)
  • Настраиваемые параметры: память, время, потоки
  • Лучшая защита от ASIC-майнеров

Совет: Используйте PASSWORD_ARGON2ID (доступен с PHP 8.1) — он сочетает защиту от side-channel атак (как Argon2i) и от GPU (как Argon2d). Если не уверены, начните с bcrypt — он проверен годами и безопасен при правильной настройке cost.