Когда я только начинал программировать на PHP, мне казалось, что аутентификация — это что-то сложное и доступное только гуру. Но на самом деле всё проще, чем кажется. В этой статье я покажу, как создать полноценную систему входа на сайт с нуля, без фреймворков и лишних библиотек. Ты научишься хешировать пароли, работать с сессиями и защищать свой код от злоумышленников. Поехали!

Digital-студия WNDER

Ссылка на оригинальную статью

Почему стоит написать свою систему, а не взять готовую?

Сегодня есть куча готовых решений: Laravel, Symfony, готовые скрипты на GitHub. Но когда ты пишешь сам, ты понимаешь, как всё работает изнутри. Это как собрать велосипед своими руками: ты знаешь каждый винтик и можешь починить его в любой момент. Плюс, своя система легче адаптируется под конкретные задачи.

Но если ты не хочешь изобретать велосипед, можно использовать библиотеки вроде PasswordHash или phpass. Однако для обучения и полного контроля лучше сделать всё самостоятельно.

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

Первое, что нужно понять — пароли нельзя хранить в открытом виде. Если база данных утечёт, злоумышленник получит доступ ко всем аккаунтам. Поэтому мы используем хеширование. Хеш — это строка, которая получается из пароля с помощью специального алгоритма. Обратно его расшифровать нельзя.

В PHP есть отличная функция password_hash(). Она автоматически генерирует соль и использует алгоритм bcrypt. Вот как это работает:

$password = 'qwerty123';
$hash = password_hash($password, PASSWORD_DEFAULT);
echo $hash; // $2y$10$...

А для проверки пароля при входе используем password_verify():

if (password_verify($password, $hash)) {
    echo 'Пароль верный!';
} else {
    echo 'Неверный пароль!';
}

Важно: не используй md5 или sha1 для паролей — они слишком быстрые и легко взламываются. Bcrypt — золотой стандарт.

Правило: всегда используй password_hash() и password_verify(). Никогда не храни пароли в открытом виде или в md5.

Создание базы данных и подключение

Для хранения пользователей нам нужна таблица в базе данных. Создадим простую таблицу users с минимальными полями:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Теперь создадим файл db.php для подключения к базе через PDO:

$host = 'localhost';
$dbname = 'myapp';
$user = 'root';
$pass = '';

try {
    $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $user, $pass);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
    die('Ошибка подключения: ' . $e->getMessage());
}

PDO — это удобный способ работы с БД, он защищает от SQL-инъекций, если использовать подготовленные выражения.

Регистрация пользователя

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

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $email = trim($_POST['email']);
    $password = $_POST['password'];

    // Простейшая валидация
    if (strlen($username) < 3 || strlen($username) > 50) {
        $error = 'Имя пользователя должно быть от 3 до 50 символов';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $error = 'Некорректный email';
    } elseif (strlen($password) < 6) {
        $error = 'Пароль должен быть не менее 6 символов';
    } else {
        // Проверяем, нет ли уже такого пользователя
        $stmt = $pdo->prepare('SELECT id FROM users WHERE username = ? OR email = ?');
        $stmt->execute([$username, $email]);
        if ($stmt->fetch()) {
            $error = 'Пользователь с таким именем или email уже существует';
        } else {
            // Хешируем пароль и вставляем
            $hash = password_hash($password, PASSWORD_DEFAULT);
            $stmt = $pdo->prepare('INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)');
            $stmt->execute([$username, $email, $hash]);
            header('Location: login.php');
            exit;
        }
    }
}

Обрати внимание: мы используем подготовленные выражения — это защита от SQL-инъекций.

Авторизация и сессии

Теперь самое интересное — вход. После проверки пароля мы сохраняем ID пользователя в сессию. Сессия — это временное хранилище на сервере, которое сохраняется между запросами.

session_start(); // В начале каждого файла, где нужна сессия

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    // Ищем пользователя
    $stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
    $stmt->execute([$username]);
    $user = $stmt->fetch();

    if ($user && password_verify($password, $user['password_hash'])) {
        // Успешный вход
        $_SESSION['user_id'] = $user['id'];
        $_SESSION['username'] = $user['username'];
        header('Location: profile.php');
        exit;
    } else {
        $error = 'Неверное имя пользователя или пароль';
    }
}

Теперь на странице профиля мы можем проверять, авторизован ли пользователь:

session_start();
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}
echo 'Привет, ' . htmlspecialchars($_SESSION['username']);

И не забываем про выход — просто удаляем сессию:

session_start();
session_unset();
session_destroy();
header('Location: login.php');
exit;
Лайфхак: чтобы защититься от фиксации сессии, после входа вызовите session_regenerate_id(true). Это меняет ID сессии и делает её более безопасной.

Защита от CSRF-атак

CSRF (Cross-Site Request Forgery) — это когда злоумышленник отправляет запрос от имени авторизованного пользователя. Например, можно вставить на стороннем сайте ссылку на смену пароля. Чтобы этого избежать, используем CSRF-токены.

Генерируем токен при создании формы и проверяем его при отправке.

// Генерация токена
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));

// В форме
<input type="hidden" name="csrf_token" value="<?php echo $_SESSION['csrf_token']; ?>">

// При обработке
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
    die('CSRF token mismatch');
}

Это добавляет ещё один слой защиты. Стоит использовать для всех POST-запросов.

Дополнительные советы по безопасности

Вот несколько правил, которые стоит соблюдать:

  • Используйте HTTPS — без него вся защита бессмысленна, пароли передаются открыто.
  • Установите ограничение на количество попыток входа, чтобы защититься от перебора.
  • Регулярно обновляйте PHP и библиотеки.
  • Не выводите ошибки на экран в production-режиме.

Для ограничения попыток можно использовать простую логику:

// В таблице users добавить поле failed_attempts и last_attempt_time
// При неудачном входе увеличивать счётчик, при успешном — обнулять

Итоги

Мы создали базовую систему аутентификации на PHP. Она включает регистрацию, вход, выход, защиту паролей и CSRF. Конечно, для продакшена нужно добавить ещё много всего, но база у тебя уже есть.

Главное — помни: безопасность — это не разовая акция, а постоянный процесс. Всегда следи за новыми уязвимостями и улучшай свой код.

Удачи в разработке!

Студия WNDER