Управление паролями в PHP: надёжные методы входа

Раздел: Безопасность в PHP -> Управление паролями в PHP

Основы безопасности паролей в PHP

При создании системы логина необходимо обеспечить защиту учётных данных пользователей. Главная цель – сохранить пароль в таком виде, чтобы даже при утечке базы данных злоумышленник не мог восстановить исходный пароль. Для этого применяют хеширование с солью и медленные алгоритмы, такие как bcrypt или Argon2.

Рекомендуемое решение: password_hash и password_verify

Стандартная функция PHP password_hash() создаёт криптографически стойкий хеш, используя алгоритм bcrypt по умолчанию. Второй вариант – явно указать PASSWORD_ARGON2ID для ещё более современного алгоритма.

Как правильно реализовать регистрацию и вход?

Регистрация:

$password = 'securePass123';
$hash = password_hash($password, PASSWORD_DEFAULT);
// Сохраняем $hash в базу данных (столбец VARCHAR(255))

Php логин и пароль вход (логин и пароль для входа в php)

Вход:

// Получаем $hash из БД по email/логину
if (password_verify($inputPassword, $hash)) {
    // пароль верен – начинаем сессию
    $_SESSION['user_id'] = $userId;
} else {
    // неверный пароль – ошибка
}

Php пароль на вход (пароль для входа в php)

Каждый вызов password_hash() автоматически генерирует случайную соль, делает хеш медленным (cost-фактор) и устойчивым к атакам перебором.

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

  • Хранение слишком короткого поля (нужно VARCHAR(255) или CHAR(60) для bcrypt).
  • Проверка пароля без password_verify (например, через прямое сравнение хешей).
  • Использование устаревших алгоритмов (MD5, SHA1) – уязвимость к радужным таблицам.

Альтернативные подходы и устаревшие методы

Почему нельзя хранить пароль в открытом виде?

// ОПАСНО! Никогда так не делать
if ($inputPassword === $plainPasswordFromDB) { ... }

Php хеширования пароля (хеширование пароля в php)

Цель: демонстрация небезопасного подхода. При утечке базы все пароли раскрыты. Использование: ни в коем случае не применять.

Проблема: отсутствие какой-либо защиты. Решение – всегда хешировать.

Как защититься от радужных таблиц, используя соль вручную?

$salt = bin2hex(random_bytes(16));
$hash = sha1($salt . $password);
// Сохраняем $salt и $hash

Этот метод устарел. Цель: понимание концепции соли, но на практике лучше использовать password_hash(), которая делает всё автоматически.

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

Какие ещё алгоритмы поддерживаются в PHP?

PHP 7.4+ поддерживает PASSWORD_ARGON2ID. Пример:

$hash = password_hash($password, PASSWORD_ARGON2ID);

Argon2 (особенно Argon2id) устойчив к атакам на GPU и является современным стандартом (переможец конкурса Password Hashing). Использование: для новых проектов с PHP 7.2+.

Проблемы: Argon2 требует установки libsodium или настройки PHP. Если среда не поддерживает, bcrypt – отличный запасной вариант.

Двухфакторная аутентификация (2FA) как дополнительный уровень

Цель: защита даже при утечке пароля. Обычно реализуется через TOTP (Google Authenticator). Случаи: банки, соцсети, сервисы с конфиденциальными данными.

// Генерация секретного ключа
$secret = 'JBSWY3DPEHPK3PXP'; // пример Base32
// Пользователь вводит одноразовый код
$userCode = '123456';
if (verifyTOTP($secret, $userCode)) { ... }

Библиотеки: sonata-project/GoogleAuthenticator или PHPGangsta/GoogleAuthenticator.

Типичные ошибки: несоответствие времени на сервере и клиенте, неправильная настройка временного окна.

Расширенные примеры реализации логина и пароля

Полный пример регистрации с валидацией и хешированием (PDO + prepared statements)

Пример
<?php
// register.php
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$email = $_POST['email'];
$password = $_POST['password'];
$passwordConfirm = $_POST['password_confirm'];

// Валидация
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
    die('Некорректный email');
}
if ($password !== $passwordConfirm) {
    die('Пароли не совпадают');
}
if (strlen($password) < 8) {
    die('Пароль должен содержать минимум 8 символов');
}

// Хеширование
$hash = password_hash($password, PASSWORD_ARGON2ID);

// Сохранение в БД
$stmt = $db->prepare('INSERT INTO users (email, password_hash) VALUES (?, ?)');
$stmt->execute([$email, $hash]);

echo 'Регистрация прошла успешно';
Регистрация прошла успешно

Скрипт входа с проверкой пароля и сессией

Пример
<?php
session_start();
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');

$email = $_POST['email'];
$password = $_POST['password'];

$stmt = $db->prepare('SELECT id, password_hash FROM users WHERE email = ?');
$stmt->execute([$email]);
$user = $stmt->fetch();

if ($user && password_verify($password, $user['password_hash'])) {
    $_SESSION['user_id'] = $user['id'];
    header('Location: dashboard.php');
    exit;
} else {
    echo 'Неверный email или пароль';
}
Неверный email или пароль

Проверка пароля на утечку через Have I Been Pwned API

Пример
function isPasswordPwned($password) {
    $sha1 = strtoupper(sha1($password));
    $prefix = substr($sha1, 0, 5);
    $suffix = substr($sha1, 5);

    $ch = curl_init("https://api.pwnedpasswords.com/range/{$prefix}");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $response = curl_exec($ch);
    curl_close($ch);

    $hashes = explode("\r\n", $response);
    foreach ($hashes as $line) {
        if (strpos($line, $suffix) === 0) {
            return true; // пароль найден в утечках
        }
    }
    return false;
}

if (isPasswordPwned($password)) {
    echo 'Этот пароль был скомпрометирован. Выберите другой.';
}
Этот пароль был скомпрометирован. Выберите другой.

Ограничение количества попыток входа (throttling)

Пример
// Используем файл или БД для хранения попыток
$ip = $_SERVER['REMOTE_ADDR'];
$attemptsFile = sys_get_temp_dir() . '/login_attempts_' . md5($ip);

$attempts = @file_get_contents($attemptsFile) ?: 0;
$attempts = (int)$attempts;

if ($attempts >= 5) {
    die('Слишком много попыток. Повторите через 15 минут.');
}

// ... обычная проверка пароля
if (!password_verify($password, $hash)) {
    $attempts++;
    file_put_contents($attemptsFile, $attempts);
    // устанавливаем время жизни файла 15 минут
    echo 'Неверный пароль';
} else {
    unlink($attemptsFile); // сброс после удачного входа
}
Неверный пароль

Использование JWT для stateless аутентификации (API)

Пример
// Установка через composer: firebase/php-jwt
use Firebase\JWT\JWT;

function createToken($userId) {
    $key = 'secret-key-123';
    $payload = [
        'iss' => 'myapp',
        'sub' => $userId,
        'iat' => time(),
        'exp' => time() + 3600 // 1 час
    ];
    return JWT::encode($payload, $key, 'HS256');
}

// Проверка токена
function verifyToken($token) {
    try {
        $decoded = JWT::decode($token, new \Firebase\JWT\Key('secret-key-123', 'HS256'));
        return $decoded->sub;
    } catch (Exception $e) {
        return null;
    }
}
Токен создан: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...

Логин и пароль для входа в PHP - comments

En
Php логин и пароль вход (php)