Аутентификация через HTTP протокол в PHP скриптах

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

Основные способы аутентификации через HTTP в PHP

Как реализовать базовую HTTP-аутентификацию с помощью PHP наиболее эффективно?

Наиболее эффективное решение - использование встроенной поддержки HTTP Basic Auth в PHP. Оно не требует внешних библиотек и легко защищает отдельные страницы или целые директории. Сервер отправляет заголовок WWW-Authenticate, браузер показывает диалог ввода логина и пароля, а PHP получает данные через суперглобальные переменные $_SERVER['PHP_AUTH_USER'] и $_SERVER['PHP_AUTH_PW'].


<?php
// Простейшая проверка логина и пароля
$valid_user = 'admin';
$valid_pass = 'secret123';

if (!isset($_SERVER['PHP_AUTH_USER']) ||
    $_SERVER['PHP_AUTH_USER'] !== $valid_user ||
    $_SERVER['PHP_AUTH_PW'] !== $valid_pass) {
    header('WWW-Authenticate: Basic realm="Private Area"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Доступ запрещен';
    exit;
}
?>
<!DOCTYPE html>
<html><body><p>Привет, <?= htmlspecialchars($_SERVER['PHP_AUTH_USER']) ?>!</p></body></html>
  

Типичные проблемы и их решения

  • Заголовки не отправляются, если перед ними есть вывод - убедитесь, что перед вызовом header() нет никакого вывода (echo, пробелов вне <?php ... ?>).
  • PHP_AUTH_USER не заполнен на CGI/FastCGI - добавьте в .htaccess: RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}].
  • Пароли передаются в открытом виде - используйте HTTPS, иначе пароль можно перехватить.

Как настроить аутентификацию на уровне веб-сервера (Apache) без кода PHP?

Этот подход полностью реализуется в конфигурации сервера. PHP не участвует в проверке, но может получить имя пользователя через переменную окружения $_SERVER['REMOTE_USER'].


# Файл .htaccess в защищаемой директории
AuthType Basic
AuthName "Доступ только для сотрудников"
AuthUserFile /var/www/.htpasswd
Require valid-user
  

Файл .htpasswd создается утилитой htpasswd:


htpasswd -c /var/www/.htpasswd user1
# Будет запрошен пароль
  

Ошибки при использовании .htaccess

  • 500 Internal Server Error - проверьте синтаксис директив, доступность файла паролей.
  • Имя пользователя не отображается в PHP - директива AuthType Basic передает переменную $_SERVER['REMOTE_USER'], но не PHP_AUTH_USER.

Как реализовать HTTP Digest-аутентификацию в PHP?

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


<?php
$realm = 'Защищенная зона';
$users = ['admin' => '5ebe2294ecd0e0f08eab7690d2a6ee69']; // md5('admin:secret')

if (!isset($_SERVER['PHP_AUTH_DIGEST'])) {
    header('HTTP/1.1 401 Unauthorized');
    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
    die('Требуется аутентификация');
}

// Разбор строки авторизации
$data = parseDigest($_SERVER['PHP_AUTH_DIGEST']);
if (!$data || !isset($users[$data['username']])) {
    die('Неверные учетные данные');
}

$A1 = $users[$data['username']];
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] !== $valid_response) {
    die('Неверный пароль');
}

echo 'Привет, '.htmlspecialchars($data['username']);

function parseDigest($txt) {
    $needed_parts = ['username','realm','nonce','uri','response','opaque','qop','nc','cnonce'];
    $data = [];
    preg_match_all('/(\w+)=["\']?([^"\',]+)["\']?/', $txt, $matches);
    foreach ($matches[1] as $i => $key) {
        $data[$key] = $matches[2][$i];
    }
    return $data;
}
?>
  

Проблемы Digest-аутентификации

  • Некоторые браузеры не поддерживают Digest - проверьте совместимость.
  • Сложность реализации - легко допустить ошибку в хешировании, пароль хранится в открытом виде (или MD5).

Как использовать cURL в PHP для отправки HTTP-запроса с аутентификацией?

Это вариант, когда PHP выступает клиентом и должен передать учетные данные на внешний сервер с HTTP Auth.


<?php
$url = 'https://api.example.com/data';
$username = 'apiuser';
$password = 'apikey';

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);

echo $response;
?>
  

Ошибки при работе cURL

  • Ошибка 401 - проверьте правильность логина/пароля и метод аутентификации (Basic/Digest).
  • SSL-ошибки - добавьте curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); для тестов (не для продакшена).

Расширенные примеры реализации HTTP-аутентификации в PHP

Пример 1: Проверка логина и пароля из базы данных MySQL

Вместо жёстко заданных логинов используется таблица users с хешированными паролями (bcrypt).

Пример

<?php
$db = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$login = $_SERVER['PHP_AUTH_USER'] ?? '';
$pass = $_SERVER['PHP_AUTH_PW'] ?? '';

$stmt = $db->prepare('SELECT password_hash FROM users WHERE login = ?');
$stmt->execute([$login]);
$row = $stmt->fetch();

if (!$row || !password_verify($pass, $row['password_hash'])) {
    header('WWW-Authenticate: Basic realm="Admin"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Неверный логин или пароль';
    exit;
}
// успешная аутентификация
echo 'Добро пожаловать, ' . htmlspecialchars($login);
?>
  

Результат: при вводе неверных данных возвращается 401 и диалог аутентификации, при верных - приветствие.

Пример 2: Комбинированная аутентификация (Basic + сессия)

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

Пример

<?php
session_start();
if (isset($_SESSION['authenticated']) && $_SESSION['authenticated'] === true) {
    // уже аутентифицирован
} else {
    // проверка Basic Auth
    if (!isset($_SERVER['PHP_AUTH_USER']) || !verify_user($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) {
        header('WWW-Authenticate: Basic realm="My App"');
        header('HTTP/1.0 401 Unauthorized');
        echo 'Доступ запрещён';
        exit;
    }
    $_SESSION['authenticated'] = true;
    $_SESSION['user'] = $_SERVER['PHP_AUTH_USER'];
}
echo 'Привет, ' . $_SESSION['user'];
?>
  

Пример 3: Аутентификация через Digest с использованием файла паролей .htdigest

Создание файла паролей:

Пример

htdigest -c /var/www/.htdigest "MyRealm" user1
  

Чтение и проверка в PHP:

Пример

<?php
$realm = 'MyRealm';
$htdigest_file = '/var/www/.htdigest';

function getDigestPassword($user, $realm, $file) {
    $lines = file($file, FILE_IGNORE_NEW_LINES);
    foreach ($lines as $line) {
        list($u, $r, $hash) = explode(':', $line);
        if ($u === $user && $r === $realm) return $hash;
    }
    return null;
}

if (!isset($_SERVER['PHP_AUTH_DIGEST'])) {
    header('WWW-Authenticate: Digest realm="'.$realm.'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
    header('HTTP/1.1 401 Unauthorized');
    exit;
}

$data = parseDigest($_SERVER['PHP_AUTH_DIGEST']);
$stored_hash = getDigestPassword($data['username'], $realm, $htdigest_file);
if (!$stored_hash) die('Пользователь не найден');

$A1 = $stored_hash;
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] !== $valid) die('Неверный пароль');

echo 'Доступ разрешён для '.htmlspecialchars($data['username']);
?>
  

Пример 4: Аутентификация через HTTP с помощью Stream Context (без cURL)

Пример

<?php
$url = 'https://api.example.com/data';
$auth = base64_encode('user:password');
$opts = [
    'http' => [
        'header' => "Authorization: Basic $auth"
    ]
];
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
if ($result === false) {
    echo 'Ошибка получения данных';
} else {
    echo $result;
}
?>
  

Результат: запрос выполняется с базовой аутентификацией, ответ сервера выводится.

Аутентификация через HTTP в PHP - comments

En
Http login php (php)