Логин пользователя в PHP: от простого к сложному

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

Основы логина пользователя на PHP

При разработке системы управления пользователями важнейшим компонентом является безопасная аутентификация. Рассмотрим различные способы реализации логина с акцентом на современные и безопасные подходы.

Безопасный логин с использованием PDO и password_hash

Как сделать логин с хешированием паролей и защитой от SQL-инъекций?

Этот метод считается основным для веб-приложений. Используются подготовленные запросы PDO и функции password_hash/password_verify.

Пример регистрации (хеширование пароля):


// registration.php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->prepare('INSERT INTO users (email, password) VALUES (:email, :password)');
$userEmail = $_POST['email'];
$userPassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
$stmt->execute(['email' => $userEmail, 'password' => $userPassword]);

Пример логина (проверка пароля):


// login.php
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $_POST['email']]);
$user = $stmt->fetch();
if ($user && password_verify($_POST['password'], $user['password'])) {
    session_start();
    $_SESSION['user_id'] = $user['id'];
    // регенерация сессии для защиты
    session_regenerate_id();
    // перенаправление
    header('Location: profile.php');
    exit;
} else {
    echo 'Неверный email или пароль.';
}

Пояснение шагов: Хеш пароля сохраняется в БД, при проверке используется password_verify, которая автоматически определяет алгоритм. PDO предотвращает SQL-инъекции. Регенерация сессии после входа снижает риск фиксации сессии.

Возможные проблемы: Неправильная настройка PDO (отсутствие исключений), утечка хеша пароля при ошибке в коде, забытая регенерация сессии, отсутствие проверки на CSRF в форме логина. Рекомендуется также использовать HTTPS.

Вариант 1: Логин без хеширования (хранение пароля в открытом виде)

Как сделать логин, если пароль хранится как есть?

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


$query = "SELECT * FROM users WHERE email='".$_POST['email']."' AND password='".$_POST['password']."'";
$result = mysqli_query($conn, $query);
if (mysqli_num_rows($result) > 0) { /* успех */ }

Проблемы: SQL-инъекции, хранение паролей в открытом виде, отсутствие защиты от перебора.

Вариант 2: Логин с устаревшим хешированием (md5, sha1)

Как использовать md5 для хранения паролей?

Ранее применялся md5, но сейчас он считается небезопасным из-за коллизий и радужных таблиц. Пример:


$password = md5($_POST['password'] . 'salt');
$query = "SELECT * FROM users WHERE password='$password'";

Проблемы: Уязвимость к коллизиям, быстрое вычисление хеша, необходимость соли. Современные алгоритмы (bcrypt, argon2) предпочтительнее.

Вариант 3: Логин с использованием JWT (JSON Web Token) для API

Как реализовать бесcессионный логин с выдачей токена?

JWT подходит для REST API. После проверки пароля сервер генерирует токен и возвращает его клиенту. Пример на PHP с библиотекой firebase/php-jwt:


require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
$secretKey = 'your-secret-key';
$payload = [
    'user_id' => $user['id'],
    'exp' => time() + 3600
];
$token = JWT::encode($payload, $secretKey, 'HS256');
echo json_encode(['token' => $token]);

Клиент отправляет токен в заголовке Authorization. Сервер проверяет подпись и срок действия.

Проблемы: Управление секретами, отзыв токенов (черные списки), уязвимость при утечке секрета, необходимость HTTPS. Также JWT не может быть отозван мгновенно, если не использовать черные списки.

Вариант 4: Логин через OAuth (Google, Facebook)

Как добавить вход с использованием стороннего провайдера?

OAuth 2.0 позволяет пользователям входить через учетные записи Google или Facebook. Реализация требует регистрации приложения у провайдера. Пример с Google Client API:


$client = new Google\Client();
$client->setClientId('CLIENT_ID');
$client->setClientSecret('CLIENT_SECRET');
$client->setRedirectUri('http://example.com/callback');
$client->addScope('email');
// перенаправление на Google
$authUrl = $client->createAuthUrl();
header('Location: ' . $authUrl);

После возврата код обменивается на токен, получаются данные пользователя.

Проблемы: Сложность настройки, необходимость поддержки нескольких провайдеров, безопасное хранение секретов, возможное отсутствие email у пользователя.

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

Пример 1: Безопасный логин с CSRF-защитой и валидацией

Полный скрипт login.php с генерацией и проверкой CSRF-токена, фильтрацией входных данных, использованием PDO и password_verify.

Пример

<?
session_start();
// Генерация CSRF-токена при загрузке формы
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
$csrfToken = $_SESSION['csrf_token'];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Проверка CSRF
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
        die('Ошибка CSRF');
    }
    // Фильтрация
    $email = filter_var($_POST['email'], FILTER_VALIDATE_EMAIL);
    $password = $_POST['password'] ?? '';
    if (!$email || empty($password)) {
        $error = 'Введите корректный email и пароль.';
    } else {
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
        $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
        $stmt->execute(['email' => $email]);
        $user = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($user && password_verify($password, $user['password'])) {
            session_regenerate_id(true);
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['user_name'] = $user['name'];
            header('Location: dashboard.php');
            exit;
        } else {
            $error = 'Неверный email или пароль.';
        }
    }
}
?>
<form method='post'>
    <input type='hidden' name='csrf_token' value='<?= htmlspecialchars($csrfToken) ?>'>
    <label>Email: <input type='email' name='email' required></label><br>
    <label>Пароль: <input type='password' name='password' required></label><br>
    <button type='submit'>Войти</button>
    <?php if (isset($error)): ?><p><?= htmlspecialchars($error) ?></p><?php endif; ?>
</form>

Результат: При успешном входе пользователь перенаправляется на dashboard.php. При ошибке отображается сообщение. Форма защищена от CSRF-атак.

Пример 2: Функция «Запомнить меня» (Remember Me)

Расширение функциональности с помощью долгосрочной куки и токена, хранящегося в БД. Пользователь может не вводить пароль при повторном визите в течение заданного времени (например, 30 дней).

Пример

// login.php при установке флажка 'remember'
if ($user && password_verify($password, $user['password'])) {
    $_SESSION['user_id'] = $user['id'];
    if (!empty($_POST['remember'])) {
        $token = bin2hex(random_bytes(32));
        $hashedToken = hash('sha256', $token);
        // сохраняем в отдельную таблицу remember_tokens
        $stmt = $pdo->prepare('INSERT INTO remember_tokens (user_id, token_hash, expires_at) VALUES (:uid, :hash, :exp)');
        $stmt->execute([
            'uid' => $user['id'],
            'hash' => $hashedToken,
            'exp' => date('Y-m-d H:i:s', time() + 30*24*3600)
        ]);
        setcookie('remember_token', $token, time() + 30*24*3600, '/', '', true, true);
    }
    header('Location: dashboard.php');
    exit;
}
// Файл check_remember.php при заходе без сессии
if (empty($_SESSION['user_id']) && !empty($_COOKIE['remember_token'])) {
    $token = $_COOKIE['remember_token'];
    $hashedToken = hash('sha256', $token);
    $stmt = $pdo->prepare('SELECT * FROM remember_tokens WHERE token_hash = :hash AND expires_at > NOW()');
    $stmt->execute(['hash' => $hashedToken]);
    $record = $stmt->fetch();
    if ($record) {
        $_SESSION['user_id'] = $record['user_id'];
        // удалить старый токен? лучше сгенерировать новый
    }
}

Результат: При выборе опции «Запомнить меня» и успешном входе устанавливается кука с токеном, по которой сессия восстанавливается при следующем визите. Токен в БД хранится в виде хеша, что безопасно при утечке.

- User php id (id пользователя в php)
- Index php user (пользователь в index.php)
- User php name (имя пользователя в php)
- User new php (новый пользователь в php)
- Online online user php (онлайн пользователь в php)
- Action profile profile php (действие профиля в php)
- User php login (логин пользователя в php)
- Php user error (ошибка пользователя в php)
- Users php (управление пользователями в php)
- Php su (php su (переключение пользователя))
- Index login php lang (форма входа на php с выбором языка)
- Groups php (группы пользователей в php)

Логин пользователя в PHP - comments

En
User php login (php)