Как правильно использовать bcrypt для хеширования паролей в PHP?
Всем привет. Я начинающий PHP-разработчик, и сейчас работаю над системой регистрации и авторизации пользователей. Почитал, что хранить пароли в открытом виде — это очень плохая практика, и нужно использовать хеширование. Нашёл информацию про bcrypt, но не совсем понимаю, как его правильно применять в PHP.
Пробовал использовать password_hash() и password_verify(), но не уверен, что делаю всё правильно. Вот пример моего кода:
<?php
// Регистрация
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_DEFAULT);
// Сохраняю $hash в базу данных...
// Авторизация
$input_password = $_POST['password'];
$stored_hash = // получаю из базы
if (password_verify($input_password, $stored_hash)) {
echo "Успешно!";
} else {
echo "Неверный пароль";
}
?>
У меня несколько вопросов:
- Достаточно ли просто использовать
PASSWORD_DEFAULTили нужно явно указыватьPASSWORD_BCRYPT? - Как настроить "стоимость" (cost factor) bcrypt? Я слышал, что это влияет на безопасность.
- Нужно ли добавлять "соль" (salt) вручную? Или PHP делает это автоматически?
- Как часто нужно обновлять хеши? И что делать, если алгоритм устареет?
- Есть ли какие-то подводные камни при работе с bcrypt в PHP?
Буду благодарен за подробное объяснение с примерами. Заранее спасибо!
<<Подробное руководство по bcrypt в PHP
Вы на правильном пути! Использование password_hash() и password_verify() — это современный и безопасный подход. Давайте разберём все ваши вопросы по порядку.
1. PASSWORD_DEFAULT vs PASSWORD_BCRYPT
На текущий момент PASSWORD_DEFAULT использует именно bcrypt (с PHP 5.5.0). Однако в будущих версиях PHP алгоритм может измениться на более мощный. Если вы хотите быть уверены, что используете bcrypt всегда, указывайте PASSWORD_BCRYPT явно:
$hash = password_hash($password, PASSWORD_BCRYPT);
2. Настройка "стоимости" (cost factor)
Cost factor определяет, сколько времени будет потрачено на вычисление хеша. Чем выше значение, тем сложнее взломать хеш методом перебора. Значение по умолчанию — 10. Для современных серверов рекомендуется 12-14. Настраивается через третий параметр — массив опций:
$options = [
'cost' => 12
];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);
Важно: Слишком высокий cost (например, 16+) может создать нагрузку на сервер. Оптимальное значение — то, при котором хеширование занимает 0.1-0.5 секунды. Протестируйте на своём сервере:
$time_start = microtime(true);
password_hash('test', PASSWORD_BCRYPT, ['cost' => 12]);
$time_end = microtime(true);
echo "Время: " . ($time_end - $time_start) . " сек";
3. Соль (salt) — автоматически
PHP автоматически генерирует криптографически безопасную случайную соль для каждого хеша. Вам не нужно добавлять её вручную. Соль хранится внутри самого хеша, поэтому password_verify() может её извлечь и использовать для проверки. Никогда не пытайтесь задать свою соль — это снизит безопасность.
4. Обновление хешей при смене алгоритма
PHP предоставляет функцию password_needs_rehash(), которая проверяет, соответствует ли хеш текущим настройкам. Это полезно, когда вы повышаете cost или меняете алгоритм. Реализуйте проверку при авторизации:
if (password_verify($password, $hash)) {
if (password_needs_rehash($hash, PASSWORD_BCRYPT, ['cost' => 12])) {
$newHash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
// Обновите хеш в базе данных
}
// Продолжайте авторизацию
}
5. Подводные камни и best practices
- Длина пароля: bcrypt ограничен 72 символами. Более длинные пароли будут обрезаться. Если нужны пароли длиннее, сначала пропустите их через обычный хеш (например, SHA256), а затем через bcrypt. Но для большинства случаев 72 символов достаточно.
- Кодировка: bcrypt работает с байтами, поэтому убедитесь, что используете одинаковую кодировку (UTF-8) при регистрации и авторизации.
- Никогда не выводите хеш пользователю: Хеш — это внутренняя информация. Не показывайте его в логах, ошибках или на экране.
- Используйте HTTPS: Хеширование защищает пароль в базе данных, но не при передаче. Всегда используйте HTTPS для защиты данных в пути.
- Не используйте устаревшие функции: Избегайте
md5(),sha1(),crypt()без bcrypt, а также собственных алгоритмов хеширования.
Полный пример класса для работы с паролями
<?php
class PasswordManager {
private $algo = PASSWORD_BCRYPT;
private $options = ['cost' => 12];
public function hash($password) {
return password_hash($password, $this->algo, $this->options);
}
public function verify($password, $hash) {
return password_verify($password, $hash);
}
public function needsRehash($hash) {
return password_needs_rehash($hash, $this->algo, $this->options);
}
}
// Использование
$pm = new PasswordManager();
$hash = $pm->hash('user_password');
// Сохраните $hash в БД
// При авторизации
if ($pm->verify($input_password, $stored_hash)) {
if ($pm->needsRehash($stored_hash)) {
$newHash = $pm->hash($input_password);
// Обновите запись в БД
}
echo "Добро пожаловать!";
}
?>
Этот подход гарантирует, что ваши пароли будут в безопасности, а система сможет адаптироваться к будущим улучшениям алгоритмов хеширования.
<<Альтернативный подход: использование библиотеки для совместимости со старыми версиями PHP
Если ваш проект работает на PHP версии ниже 5.5.0, где нет встроенных функций password_hash() и password_verify(), вы можете использовать библиотеку password_compat от Anthony Ferrara (автора этих функций). Она добавляет обратную совместимость.
Установка через Composer:
composer require ircmaxell/password-compat
Пример использования (идентично нативным функциям):
<?php
require_once 'vendor/autoload.php';
// Хеширование
$hash = password_hash('mypassword', PASSWORD_BCRYPT, ['cost' => 10]);
// Проверка
if (password_verify('mypassword', $hash)) {
echo 'Пароль верен!';
}
?>
Дополнительные советы для максимальной безопасности:
- Используйте Bcrypt с солью: Даже если вы используете старую функцию
crypt(), убедитесь, что соль генерируется случайно для каждого пользователя. Пример:$salt = '$2y$10$' . bin2hex(random_bytes(22)); - Храните хеш в столбце типа VARCHAR(60) или CHAR(60): bcrypt хеш всегда имеет длину 60 символов. Если используете PASSWORD_DEFAULT, лучше выделить 255 символов на будущее.
- Рассмотрите Argon2: Начиная с PHP 7.2, доступен алгоритм Argon2 (через
PASSWORD_ARGON2IилиPASSWORD_ARGON2ID). Он считается более безопасным, чем bcrypt. Однако bcrypt остаётся отличным выбором и поддерживается повсеместно.
Помните: безопасность паролей — это не разовая настройка, а постоянный процесс. Регулярно проверяйте, используете ли вы современные алгоритмы и обновляйте cost factor по мере роста производительности серверов.