Создание системы логина на PHP

Раздел: PHP -> Веб-разработка на PHP

Основные подходы к реализации входа

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

Этот способ является базовым и рекомендуемым для большинства проектов. Он использует расширение PDO для взаимодействия с базой данных, функцию password_verify для проверки хеша пароля и механизм сессий для поддержания состояния аутентификации.


// config.php
$host = 'localhost';
$db   = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
];
try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    die('Подключение не удалось: ' . $e->getMessage());
}
  

App path php (работа с путями файлов в php)


// login.php
session_start();
require 'config.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = $_POST['email'] ?? '';
    $password = $_POST['password'] ?? '';
    
    $stmt = $pdo->prepare('SELECT id, password FROM users WHERE email = ?');
    $stmt->execute([$email]);
    $user = $stmt->fetch();
    
    if ($user && password_verify($password, $user['password'])) {
        $_SESSION['user_id'] = $user['id'];
        header('Location: dashboard.php');
        exit;
    } else {
        $error = 'Неверный email или пароль';
    }
}
  

App php domain (работа с доменами в php)

Возможные проблемы:

  • Отсутствие проверки входящих данных может привести к XSS-атакам. Рекомендуется фильтровать и экранировать вывод.
  • Слабая обработка ошибок PDO: исключение может раскрыть структуру БД. В production используйте логирование.
  • Не забывайте вызывать session_start() перед любым выводом.

Как сделать вход с использованием MySQLi вместо PDO?

MySQLi также позволяет выполнять подготовленные запросы, но его API менее универсален, чем PDO. Решение подходит для проектов, уже использующих MySQLi.


$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
    die('Ошибка подключения: ' . $mysqli->connect_error);
}

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

$stmt = $mysqli->prepare('SELECT id, password FROM users WHERE email = ?');
$stmt->bind_param('s', $email);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();

if ($user && password_verify($password, $user['password'])) {
    $_SESSION['user_id'] = $user['id'];
    // перенаправление
}
  

Http user agent php (получение user-agent в php)

Типичная ошибка: забыть вызвать bind_param с правильными типами. Это приводит к непредсказуемому поведению запроса.

Как реализовать вход с устаревшим хешированием (md5)?

Этот вариант рассматривается только для обратной совместимости старых систем. Использование md5 или sha1 без соли крайне небезопасно.


// Пример (не рекомендуется)
$hash = md5($password);
$stmt = $pdo->prepare('SELECT id FROM users WHERE email = ? AND password = ?');
$stmt->execute([$email, $hash]);
  

Config app php (конфигурация php приложения)

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

Как внедрить вход с помощью JWT для stateless API?

JWT подходит для REST API и микросервисной архитектуры, где сервер не хранит сессии. После аутентификации клиент получает токен, который прикрепляется к каждому запросу.


// Генерация JWT после успешного входа
require __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;

$key = 'secret_key';
$payload = [
    'user_id' => $user['id'],
    'exp' => time() + 3600
];
$jwt = JWT::encode($payload, $key, 'HS256');
echo json_encode(['token' => $jwt]);
  

Ошибки: использование слабого ключа, отсутствие проверки срока действия токена, уязвимость к атакам с повторным использованием (replay). Рекомендуется хранить ключ вне кода и использовать HTTPS.

- Php create html (создание html в php)
- Default php app (настройки по умолчанию в php приложении)
- Php веб сервисы (php веб-сервисы)

Расширенные примеры и пояснения

Ниже приведены подробные примеры для углубленного понимания и защиты системы входа.

Полный скрипт login.php с защитой от CSRF и ограничением попыток

Этот пример включает генерацию CSRF-токена, проверку количества неудачных попыток и установку сессии.

Пример

// csrf_token.php
session_start();
if (empty($_SESSION['csrf_token'])) {
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}

// login.php (часть кода)
require 'csrf_token.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Проверка CSRF
    if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
        die('Недействительный CSRF-токен');
    }
    
    // Ограничение попыток (в сессии)
    $maxAttempts = 5;
    $lockoutTime = 300; // 5 минут
    
    if (isset($_SESSION['login_attempts']) && $_SESSION['login_attempts'] >= $maxAttempts) {
        $remaining = $lockoutTime - (time() - $_SESSION['last_attempt_time']);
        if ($remaining > 0) {
            die('Слишком много попыток. Попробуйте через ' . ceil($remaining / 60) . ' мин.');
        } else {
            $_SESSION['login_attempts'] = 0;
        }
    }
    
    // Логика аутентификации
    $email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
    $password = $_POST['password'] ?? '';
    
    if (!$email) {
        $error = 'Некорректный email';
    } else {
        // Проверка пароля...
        if ($user && password_verify($password, $user['password'])) {
            // Успех: сброс попыток
            unset($_SESSION['login_attempts']);
            unset($_SESSION['last_attempt_time']);
            $_SESSION['user_id'] = $user['id'];
            header('Location: dashboard.php');
            exit;
        } else {
            $_SESSION['login_attempts'] = ($_SESSION['login_attempts'] ?? 0) + 1;
            $_SESSION['last_attempt_time'] = time();
            $error = 'Неверные учетные данные';
        }
    }
}
?>

<form method="post">
    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    <input type="email" name="email" required>
    <input type="password" name="password" required>
    <button type="submit">Войти</button>
</form>
// Результат: после пяти неверных попыток в течение 5 минут пользователь блокируется.

Реализация Remember Me (запоминание на устройстве)

Добавляет долгоживущую cookie для автоматического входа. Используется токен, хранящийся в базе.

Пример

// При успешном входе с опцией "запомнить"
if (isset($_POST['remember'])) {
    $token = bin2hex(random_bytes(64));
    $hashedToken = password_hash($token, PASSWORD_DEFAULT);
    // Сохраняем хеш токена в БД вместе с user_id и меткой времени
    $stmt = $pdo->prepare('INSERT INTO user_tokens (user_id, token_hash, expires_at) VALUES (?, ?, ?)');
    $stmt->execute([$user['id'], $hashedToken, date('Y-m-d H:i:s', time() + 30*24*3600)]);
    
    setcookie('remember_token', $token, time() + 30*24*3600, '/', '', true, true);
}

// На странице проверки (init.php)
if (!isset($_SESSION['user_id']) && isset($_COOKIE['remember_token'])) {
    $token = $_COOKIE['remember_token'];
    $stmt = $pdo->prepare('SELECT user_id, token_hash FROM user_tokens WHERE expires_at > NOW()');
    $stmt->execute();
    $records = $stmt->fetchAll();
    foreach ($records as $record) {
        if (password_verify($token, $record['token_hash'])) {
            $_SESSION['user_id'] = $record['user_id'];
            // Обновляем токен для предотвращения фиксации
            break;
        }
    }
}
// Результат: пользователь автоматически входит до истечения срока токена (30 дней).

Аутентификация через OAuth (пример с Google)

Требуется библиотека Google API Client и настройка OAuth 2.0 в Google Cloud Console.

Пример

require_once __DIR__ . '/vendor/autoload.php';

$client = new Google\Client();
$client->setClientId('YOUR_CLIENT_ID');
$client->setClientSecret('YOUR_CLIENT_SECRET');
$client->setRedirectUri('http://localhost/google-callback.php');
$client->addScope('email');

// Ссылка для входа
$authUrl = $client->createAuthUrl();
echo '<a href="' . htmlspecialchars($authUrl) . '">Войти через Google</a>';

// Обработка callback (google-callback.php)
if (isset($_GET['code'])) {
    $token = $client->fetchAccessTokenWithAuthCode($_GET['code']);
    $client->setAccessToken($token);
    
    $oauth2 = new Google\Service\Oauth2($client);
    $userinfo = $oauth2->userinfo->get();
    
    // Получаем email, name и т.д.
    $email = $userinfo->email;
    // Ищем пользователя в своей БД или создаем нового
    $stmt = $pdo->prepare('SELECT id FROM users WHERE email = ?');
    $stmt->execute([$email]);
    $user = $stmt->fetch();
    if (!$user) {
        $stmt = $pdo->prepare('INSERT INTO users (email, name) VALUES (?, ?)');
        $stmt->execute([$email, $userinfo->name]);
        $userId = $pdo->lastInsertId();
    } else {
        $userId = $user['id'];
    }
    $_SESSION['user_id'] = $userId;
    header('Location: dashboard.php');
}
// Результат: пользователь перенаправляется на страницу входа Google, после подтверждения возвращается на сайт уже авторизованным.

Реализация входа пользователя в PHP - comments

En
Login php app (php)