Разработка системы входа на PHP: методы и реализации

Раздел: Веб-разработка -> Аутентификация

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

Базовая аутентификация через базу данных MySQL с использованием password_hash и password_verify

Это наиболее распространённое и безопасное решение. Оно подходит для большинства веб-проектов: личных кабинетов, CMS, интернет-магазинов. Цель - проверить учётные данные пользователя, хранящиеся в реляционной базе, и установить сессию.

Пример структуры таблицы users:


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

Admin index php login php (страница входа администратора php)

Регистрация пользователя (фрагмент register.php):


<?php
$password = $_POST['password'];
$hash = password_hash($password, PASSWORD_BCRYPT);
// Сохранение $hash в базу
?>
  

Php code login (код страницы входа php)

Страница входа (login.php):


<?php
session_start();
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$stmt = $db->prepare('SELECT * FROM users WHERE username = :username');
$stmt->execute(['username' => $_POST['username']]);
$user = $stmt->fetch();
if ($user && password_verify($_POST['password'], $user['password_hash'])) {
    $_SESSION['user_id'] = $user['id'];
    $_SESSION['username'] = $user['username'];
    header('Location: dashboard.php');
} else {
    echo 'Неверное имя пользователя или пароль';
}
?>
  

Request login php (запрос на вход php)

Пояснение: password_hash создаёт bcrypt-хэш с солью, password_verify проверяет пароль относительно хэша. Использование подготовленных запросов предотвращает SQL-инъекции.

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

  • Хранение паролей в открытом виде (никогда не делать).
  • Использование устаревших алгоритмов (md5, sha1).
  • Отсутствие session_start() - сессия не создастся.
  • Вывод слишком конкретных сообщений об ошибке («неверный пароль») - облегчает подбор.

Как исправить:

  • Всегда использовать password_hash с PASSWORD_BCRYPT или PASSWORD_ARGON2ID.
  • При неудаче выводить общее сообщение «Неверные учётные данные».
  • Применять session_regenerate_id() после успешного входа для защиты от фиксации сессии.

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

Вариант 1: Хранение учётных данных в файле (JSON, PHP-массиве)

Подходит для прототипов, небольших проектов без доступа к СУБД. Данные хранятся, например, в users.json.


// users.json
[
  {
    "username": "admin",
    "password_hash": "$2y$10$..."
  }
]
  

Localhost register php (регистрация на локальном сервере)


<?php
$users = json_decode(file_get_contents('users.json'), true);
$found = false;
foreach ($users as $user) {
    if ($user['username'] === $_POST['username'] && password_verify($_POST['password'], $user['password_hash'])) {
        $found = true;
        break;
    }
}
if ($found) {
    // установка сессии
} else {
    // ошибка
}
?>
  

регистрации php mysql (регистрация пользователей на php и mysql)

Проблемы:

  • Файл может быть прочитан злоумышленником при неправильной конфигурации сервера (необходимо размещать вне document root).
  • Параллельный доступ приводит к состоянию гонки.
  • Масштабирование затруднено.

Решение: для реальных проектов этот метод не рекомендуется, но для локального тестирования допустим.

Как интегрировать вход с корпоративным LDAP сервером?

Вариант 2: Аутентификация через LDAP

Используется в корпоративных средах, где учётные записи хранятся в Active Directory или OpenLDAP.


<?php
$ldapconn = ldap_connect("ldap://example.com");
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
$bind = @ldap_bind($ldapconn, "uid=" . $_POST['username'] . ",ou=users,dc=example,dc=com", $_POST['password']);
if ($bind) {
    // успешно
} else {
    // ошибка
}
ldap_close($ldapconn);
?>
  

Service login php (сервис аутентификации php)

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

  • Неверно указан DN (Distinguished Name) - нужно точно соответствовать структуре каталога.
  • Отсутствует расширение LDAP в PHP.
  • Не обрабатываются случаи, когда сервер LDAP недоступен.

Совет: используйте ldap_search для поиска пользователя по любому атрибуту, затем пробуйте bind с найденным DN.

Как добавить вход через сторонние сервисы (OAuth)?

Вариант 3: OAuth 2.0 (Google, GitHub, Facebook)

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

Пример с использованием библиотеки league/oauth2-google (composer):


<?php
$provider = new \League\OAuth2\Client\Provider\Google([
    'clientId'     => 'CLIENT_ID',
    'clientSecret' => 'CLIENT_SECRET',
    'redirectUri'  => 'https://example.com/callback.php',
]);
// Перенаправление на Google
if (!isset($_GET['code'])) {
    $authUrl = $provider->getAuthorizationUrl();
    $_SESSION['oauth2state'] = $provider->getState();
    header('Location: ' . $authUrl);
    exit;
}
// Обратный вызов
if (empty($_GET['state']) || $_GET['state'] !== $_SESSION['oauth2state']) {
    unset($_SESSION['oauth2state']);
    exit('Invalid state');
}
$token = $provider->getAccessToken('authorization_code', [
    'code' => $_GET['code']
]);
$user = $provider->getResourceOwner($token);
echo 'Hello, ' . $user->getName();
?>
  

Login php file (файл входа php)

Проблемы:

  • Необходимо зарегистрировать приложение у провайдера (clientId, clientSecret).
  • Нужно обрабатывать ошибки, если пользователь отклонил доступ.
  • Важно проверять state для защиты от CSRF.

Рекомендация: использовать готовые библиотеки (oauth2-client) и хранить токены в сессии.

Как реализовать stateless аутентификацию через токены (JWT)?

Вариант 4: API-аутентификация с помощью JWT

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


<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$key = 'secret_key';
$payload = [
    'sub' => 123,
    'username' => 'user',
    'iat' => time(),
    'exp' => time() + 3600
];
$jwt = JWT::encode($payload, $key, 'HS256');
echo $jwt;
// Проверка токена на защищённых маршрутах:
$token = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION']);
try {
    $decoded = JWT::decode($token, new Key($key, 'HS256'));
    echo 'Доступ разрешён для пользователя ' . $decoded->username;
} catch (Exception $e) {
    http_response_code(401);
    echo 'Unauthorized';
}
?>
  

Index php id login (авторизация через параметр id в index.php)

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

  • Токены не передаются по HTTPS - перехват.
  • Не проверяется срок действия (exp) - токены бессрочны.
  • Секретный ключ слишком простой или хранится в коде.

Решение: использовать библиотеку firebase/php-jwt, устанавливать короткое время жизни, хранить ключ в конфигурации вне document root.

Как реализовать функцию «Запомнить меня» с безопасным хранением?

Вариант 5: Постоянная аутентификация через куки (remember me)

Используется для удобства пользователей. Генерируется случайный токен, который хранится в куке и в базе данных вместе с идентификатором пользователя.


<?php
if ($_POST['remember']) {
    $token = bin2hex(random_bytes(32));
    $stmt = $db->prepare('UPDATE users SET remember_token = :token WHERE id = :id');
    $stmt->execute(['token' => hash('sha256', $token), 'id' => $userId]);
    setcookie('remember', $token, time() + 86400 * 30, '/', '', true, true);
}
// Проверка при отсутствии сессии:
if ($cookie = $_COOKIE['remember'] ?? null) {
    $stmt = $db->prepare('SELECT id, username FROM users WHERE remember_token = :token');
    $stmt->execute(['token' => hash('sha256', $cookie)]);
    $user = $stmt->fetch();
    if ($user) {
        // установка сессии
    }
}
?>
  

Проблемы:

  • Токен может быть украден через XSS (кука должна иметь флаг HttpOnly).
  • Если токен скомпрометирован, злоумышленник может войти (решение: сделать токен одноразовым или добавить проверку User-Agent/IP).

Улучшение: хранить две куки - series (постоянный идентификатор) и token (одноразовый), обновлять их при каждом входе.

- Login php src (исходный код страницы входа php)
- Index php r login (логин (авторизация) в php)
- Login php forget (восстановление пароля в php)

Расширенные примеры кода для аутентификации на PHP

1. Вход с блокировкой после неудачных попыток (brute-force защита)

Цель - предотвратить перебор паролей. Необходимо сохранять количество неудачных попыток и время блокировки (например, в базе данных или в файле).

Пример

<?php
// Таблица для отслеживания попыток
CREATE TABLE login_attempts (
    ip VARCHAR(45) NOT NULL,
    attempt_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

// В обработчике входа
$ip = $_SERVER['REMOTE_ADDR'];
$stmt = $db->prepare('SELECT COUNT(*) FROM login_attempts WHERE ip = :ip AND attempt_time > (NOW() - INTERVAL 15 MINUTE)');
$stmt->execute(['ip' => $ip]);
if ($stmt->fetchColumn() >= 5) {
    die('Слишком много попыток. Попробуйте через 15 минут.');
}

// Если пароль неверен
if (!$auth_ok) {
    $stmt = $db->prepare('INSERT INTO login_attempts (ip) VALUES (:ip)');
    $stmt->execute(['ip' => $ip]);
    echo 'Неверные учётные данные.';
}
?>
Результат: после пяти неудачных попыток с одного IP за 15 минут ввод блокируется.

2. Вход с капчей (Google reCAPTCHA v3)

Используется для дополнительной защиты от ботов. После успешной проверки пароля, но перед установкой сессии, выполняется проверка reCAPTCHA.

Пример

<?php
$recaptchaSecret = 'ваш_секретный_ключ';
$response = $_POST['g-recaptcha-response'];
$verify = file_get_contents("https://www.google.com/recaptcha/api/siteverify?secret={$recaptchaSecret}&response={$response}");
$captchaSuccess = json_decode($verify)->success;

if ($auth_ok && $captchaSuccess) {
    // установка сессии
} elseif ($auth_ok && !$captchaSuccess) {
    echo 'Пожалуйста, подтвердите, что вы не робот.';
}
?>
Результат: капча может быть пройдена автоматически (v3) или требует клика (v2).

3. Аутентификация через REST API с JWT (полный пример)

Реализация endpoints: POST /login (возвращает JWT), GET /profile (проверяет JWT). Используется библиотека firebase/php-jwt.

Пример

// composer.json
{
    "require": {
        "firebase/php-jwt": "^6.0"
    }
}

// login.php
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;

$key = 'secret123';
$payload = [
    'sub' => 1,
    'name' => 'John',
    'iat' => time(),
    'exp' => time() + 600
];
$jwt = JWT::encode($payload, $key, 'HS256');
header('Content-Type: application/json');
echo json_encode(['token' => $jwt]);

// profile.php
$token = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION'] ?? '');
try {
    $decoded = JWT::decode($token, new Key($key, 'HS256'));
    echo json_encode(['user' => $decoded->name]);
} catch (\Exception $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid token']);
}
?>
Результат: curl -X POST http://example.com/login -> {"token":"eyJhbGciOiJIUzI1NiIs..."}
curl -H "Authorization: Bearer eyJ..." http://example.com/profile -> {"user":"John"}

4. Вход с двухфакторной аутентификацией (TOTP)

После успешного логина пользователь должен ввести одноразовый код из приложения (Google Authenticator). Используется библиотека spomky-labs/otphp.

Пример

<?php
require 'vendor/autoload.php';
use OTPHP\TOTP;

// Генерация секрета при регистрации
$totp = TOTP::create();
$secret = $totp->getSecret(); // сохранить в базу для пользователя
$qrCode = $totp->getProvisioningUri(); // показать QR-код

// Проверка кода
$totp = TOTP::create($secretFromDB);
if ($totp->verify($_POST['otp'], null, 1)) { // допускается рассинхронизация в 1 шаг
    // второй фактор пройден
} else {
    // код неверен
}
?>
Результат: пользователь сканирует QR-код, вводит 6-значный код, который меняется каждые 30 секунд.

5. Логирование всех попыток входа для аудита

Записывается IP, время, успешность входа. Полезно для безопасности и отладки.

Пример

<?php
$logFile = __DIR__ . '/logs/auth.log';
$data = [
    date('Y-m-d H:i:s'),
    $_SERVER['REMOTE_ADDR'],
    $_POST['username'],
    $auth_ok ? 'SUCCESS' : 'FAIL'
];
file_put_contents($logFile, implode(' | ', $data) . "\n", FILE_APPEND | LOCK_EX);
?>
Пример записи:
2025-03-14 10:30:45 | 192.168.1.1 | admin | FAIL
2025-03-14 10:31:02 | 192.168.1.1 | admin | SUCCESS

Код страницы входа PHP - comments

En
Php code login (php)