Правильное управление паролями при входе пользователя
Основы безопасного хранения паролей
Наиболее эффективное решение: использование password_hash() и password_verify().
Эти функции реализуют алгоритмы bcrypt, Argon2i или Argon2id (доступны с PHP 7.4). Хэш автоматически содержит соль и параметры стоимости, что исключает необходимость ручного управления солью.
<?php
// Регистрация пользователя
$plainPassword = 'qwerty123';
$hash = password_hash($plainPassword, PASSWORD_BCRYPT, ['cost' => 12]);
// Сохранить $hash в БД (например, в поле password_hash)
?>
Php логин и пароль вход (логин и пароль для входа в php)
При входе проверка происходит так:
<?php
$inputPassword = $_POST['password'];
$storedHash = '$2y$12$...'; // из БД
if (password_verify($inputPassword, $storedHash)) {
// Пароль верный
} else {
// Неверный пароль
}
?>
Php пароль на вход (пароль для входа в php)
Типичные проблемы и их решения:
- Выбор стоимости (cost). Слишком малая стоимость (например, 4) делает хэш уязвимым к атакам перебором. Рекомендуется 10–12. Для Argon2 параметры memory_cost и time_cost подбираются так, чтобы время вычисления составляло 0.1–0.5 секунды на целевом оборудовании.
- Обновление алгоритма. Если в будущем появится более стойкий алгоритм, старые хэши можно пересчитать при успешном входе с помощью
password_needs_rehash(). - Отсутствие проверки на пустой пароль. Никогда не допускать пустых паролей.
Почему нельзя хранить пароли в открытом виде?
Хранение незашифрованных паролей приводит к полной компрометации учётных записей в случае утечки базы данных. Даже внутренний сотрудник может их прочитать.
// Пример опасного кода
$password = $_POST['password'];
$sql = "INSERT INTO users (username, password) VALUES ('user', '$password')";
Php хеширования пароля (хеширование пароля в php)
Проблема:
Любая SQL-инъекция или чтение БД даёт злоумышленнику все пароли в чистом виде.Как не следует хэшировать пароли? Использование MD5
MD5 - быстрый алгоритм, не предназначенный для защиты паролей. Современные GPU вычисляют миллиарды MD5-хэшей в секунду.
$hash = md5($password); // крайне не рекомендуется
Ошибка:
Без соли два одинаковых пароля дадут одинаковый хэш (радужные таблицы). Даже с солью скорость вычисления делает перебор тривиальным.Какой способ был актуален ранее? SHA1 с солью
До появления password_hash() разработчики использовали sha1() или hash('sha256') с ручной солью. Однако скорость хэширования остаётся высокой.
$salt = bin2hex(random_bytes(16));
$hash = sha1($salt . $password); // устарело
Недостаток:
Необходимо хранить соль отдельно. Кроме того, SHA-семейство не имеет регулируемой «медлительности», поэтому перебор возможен на GPU.Как использовать bcrypt в старых версиях PHP?
До PHP 5.5 можно было использовать crypt() с префиксом '$2y$'. Однако такой подход требует ручного управления солью и проверкой.
$salt = '$2y$12$' . bin2hex(random_bytes(22));
$hash = crypt($password, $salt);
Сложность:
Легко допустить ошибку в длине соли или префиксе. Рекомендуется обновить PHP до версии, поддерживающейpassword_hash().Какой алгоритм рекомендуется для новых проектов? Argon2i или Argon2id
Argon2 - победитель конкурса PHC (Password Hashing Competition). В PHP доступен с версии 7.2 (Argon2i) и 7.4 (Argon2id). Он устойчив к атакам по сторонним каналам и позволяет точно настроить использование памяти и времени.
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 65536, // 64 MB
'time_cost' => 4,
'threads' => 2
]);
Проблемы:
На старом оборудовании высокие параметры могут вызывать чрезмерную нагрузку. Следует тестировать производительность в условиях сервера.Расширенные примеры работы с паролями
Полный цикл регистрации и аутентификации с password_hash и password_verify
// registration.php
$username = 'user123';
$password = 'SecurePass!2024';
$options = ['cost' => 12];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
if ($hash === false) {
// Обработка ошибки (например, неподдерживаемый алгоритм)
throw new \RuntimeException('Ошибка хэширования пароля');
}
// Сохранение в БД (PDO example)
$stmt = $pdo->prepare('INSERT INTO users (username, password_hash) VALUES (?, ?)');
$stmt->execute([$username, $hash]);
echo "Пользователь зарегистрирован, хэш: $hash";
Пользователь зарегистрирован, хэш: $2y$12$... (56 символов)
// login.php
$inputUser = 'user123';
$inputPass = 'SecurePass!2024';
$stmt = $pdo->prepare('SELECT password_hash FROM users WHERE username = ?');
$stmt->execute([$inputUser]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
echo "Пользователь не найден";
exit;
}
$storedHash = $row['password_hash'];
if (password_verify($inputPass, $storedHash)) {
// Проверка необходимости обновления хэша
if (password_needs_rehash($storedHash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($inputPass, PASSWORD_BCRYPT, ['cost' => 12]);
$updateStmt = $pdo->prepare('UPDATE users SET password_hash = ? WHERE username = ?');
$updateStmt->execute([$newHash, $inputUser]);
}
$_SESSION['user'] = $inputUser;
echo "Вход выполнен успешно";
} else {
echo "Неверный пароль";
}
Вход выполнен успешно
Пояснение:
При успешном входе проверяется, требует ли старый хэш обновления (например, если изменился алгоритм или стоимость). Это позволяет постепенно мигрировать на более стойкие алгоритмы.Использование Argon2id с тонкой настройкой параметров
$password = 'SuperSecure#2025';
$hash = password_hash($password, PASSWORD_ARGON2ID, [
'memory_cost' => 1024 * 128, // 128 MB
'time_cost' => 6,
'threads' => 4
]);
// Проверка
if (password_verify($password, $hash)) {
echo "Пароль верен, хэш: $hash";
}
Пароль верен, хэш: $argon2id$v=19$m=131072,t=6,p=4$... (длинная строка)
Пояснение:
Argon2id защищает от атак по времени и использует много памяти, что усложняет параллельный перебор на GPU. Параметр threads указывает количество параллельных потоков, но не все системы его поддерживают.Сравнение времени выполнения разных алгоритмов
$password = 'test123';
$start = microtime(true);
$hashMd5 = md5($password);
$timeMd5 = microtime(true) - $start;
$start = microtime(true);
$hashBcrypt = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
$timeBcrypt = microtime(true) - $start;
$start = microtime(true);
$hashArgon = password_hash($password, PASSWORD_ARGON2I, [
'memory_cost' => 1024 * 64,
'time_cost' => 2
]);
$timeArgon = microtime(true) - $start;
echo "MD5: $timeMd5 с\n";
echo "Bcrypt (cost=10): $timeBcrypt с\n";
echo "Argon2i: $timeArgon с\n";
MD5: 0.000001 с Bcrypt (cost=10): 0.08 с Argon2i: 0.12 с
Вывод:
MD5 вычисляется практически мгновенно, что делает его непригодным для паролей. Bcrypt и Argon2 имеют встроенную «медлительность», которая настраивается.Генерация случайной соли для старых методов (демонстрация не рекомендуется)
// Пример для систем, где нет password_hash
$salt = bin2hex(random_bytes(16)); // 32 символа
$hash = hash('sha256', $salt . $password);
echo "Соль: $salt, Хэш: $hash";
Соль: a1b2c3d4e5f67890... Хэш: 5e884898da280471...
Примечание:
Даже с солью SHA-256 не защищает от быстрого перебора. Единственный случай использования - поддержка легаси-систем, но в новых проектах такой подход неприемлем.