HTTP, REST API, JWT и OAuth: основы и типичные ошибки безопасности

HTTP, REST API, JWT и OAuth: основы и типичные ошибки безопасности

Обновлено 3 сентября 2026 года.

При разработке веб-интеграции нужно решать несколько разных задач: доставить запрос, проверить отправителя, определить его права и корректно обработать данные. HTTPS, токен и проверка входных полей отвечают за разные части этой работы. Наличие одного механизма не заменяет остальные.

Это руководство объясняет основные понятия и ошибки, которые встречаются при подключении CRM, BI-систем и собственных API. Примеры предназначены для понимания механики; реальные схемы аутентификации следует реализовывать средствами поддерживаемого фреймворка и библиотек.

Клиент, сервер, ресурс и API

Клиент отправляет запрос, сервер его обрабатывает и возвращает ответ. Ресурсом может быть заказ, файл или коллекция записей. URI идентифицирует ресурс; URL также описывает способ и место доступа. Один объект может быть доступен по нескольким адресам, поэтому слово «уникальный» не следует понимать как обязательное соответствие одного URL одному объекту базы.

API — интерфейс взаимодействия программ. Веб-API часто использует HTTP, но не каждый API является веб-сервисом или REST API. JSON и XML — форматы представления данных, а не способы авторизации.

HTTP и HTTPS

HTTP задаёт семантику запросов и ответов: методы, заголовки, статусы и представления ресурсов. HTTPS использует защищённое соединение с TLS. При корректной проверке сертификата клиент проверяет сервер, а соединение обеспечивает конфиденциальность и целостность передаваемых данных. OWASP: защита транспорта с TLS.

Выражение «SSL-сертификат» часто используют по привычке, но устаревшие протоколы SSL не следует включать ради этого названия. Сертификат связывает имя сервера с ключом; он не подтверждает добросовестность бизнеса и отсутствие уязвимостей в приложении.

HTTPS защищает конкретное соединение. Если TLS завершается на прокси, последующий участок до приложения настраивается отдельно. HTTPS также не скрывает секреты от самого приложения, его журналов или расширения браузера с соответствующим доступом. Поэтому токен в URL может утечь через историю и логи, даже когда передача по сети зашифрована.

Безопасные и идемпотентные методы — разные понятия

Метод Основное назначение Семантика
GET Получить представление ресурса Безопасный и идемпотентный
HEAD Получить сведения об ответе без содержимого Безопасный и идемпотентный
POST Передать данные для обработки согласно назначению ресурса Не считается идемпотентным по умолчанию
PUT Создать или заменить состояние целевого ресурса Идемпотентный
DELETE Удалить ресурс по указанному адресу Идемпотентный
PATCH Применить частичное изменение Идемпотентность зависит от операции

«Безопасный» означает, что клиент не запрашивает изменение состояния ресурса. Сервер при этом может вести журнал доступа. «Идемпотентный» означает одинаковый предполагаемый эффект одного и нескольких одинаковых запросов; ответы могут различаться. Например, повторный DELETE может вернуть 404, хотя требуемый результат — отсутствие ресурса — уже достигнут. Свойства методов в RFC 9110; PATCH, RFC 5789.

POST не шифрует данные: тело запроса защищается транспортом, а также может попасть в логи. Для создания заказа с повторными попытками нужен предусмотренный API механизм защиты от дублей, например ключ идемпотентности с сохранением результата операции. Нельзя считать любой повтор POST безопасным после тайм-аута: сервер мог уже выполнить действие.

Что означает REST и stateless

REST — архитектурный стиль, а не протокол и не синоним «HTTP плюс JSON». Его ограничения включают разделение клиента и сервера, отсутствие серверного контекста сессии между запросами, кэширование, единообразный интерфейс и слоистую систему. Передача исполняемого кода клиенту — необязательное ограничение. Описание REST у Roy Fielding.

Stateless не запрещает серверу хранить заказы, пользователей или другие бизнес-данные. Смысл в том, что запрос содержит сведения, необходимые для его обработки, и не требует восстановить неявный контекст прежних запросов клиента. JWT для этого не обязателен: формат токена и архитектурный стиль — разные решения.

Аутентификация и авторизация

Аутентификация проверяет предъявленную идентичность или учётные данные. Авторизация определяет, разрешена ли конкретная операция над конкретным объектом. Корректный токен пользователя не означает право читать любой заказ по его идентификатору.

Типовой запрос с bearer-токеном выглядит так. Это схематичный пример HTTP/1.1; вместо обозначения в угловых скобках передаётся реальный токен.

GET /api/orders/42 HTTP/1.1
Host: api.example.com
Authorization: Bearer <ACCESS_TOKEN>
Accept: application/json

Правильное имя заголовка — Authorization. Bearer означает, что предъявление токена даёт возможность использовать его права; поэтому токен нужно защищать как секрет. Проверка должна учитывать срок действия и назначение токена, а затем права на заказ 42. Не берите идентификатор текущего пользователя из произвольного параметра запроса без связи с проверенной сессией.

Читай также:  Безопасность Power BI: доступ, RLS, OLS, аудит и восстановление

JWT: декодирование не равно проверке

JWT описывает формат набора утверждений, или claims. Часто используется подписанная форма JWS с тремя частями header.payload.signature. Но JWT может быть и зашифрованным JWE; у его компактной формы пять частей. Поэтому утверждение «любой JWT — три части и никогда не шифруется» неверно. JWT, RFC 7519; JWE, RFC 7516.

В обычном подписанном JWS содержимое payload кодируется Base64url и доступно для чтения. Не помещайте туда пароль или другой секрет. Целостность может обеспечиваться MAC с общим ключом, например HMAC, либо асимметричной цифровой подписью. Это разные схемы управления ключами.

Используйте проверенную библиотеку и заранее задайте допустимые алгоритмы и доверенные ключи. Проверяйте подпись, ожидаемого издателя iss, получателя aud, сроки exp/nbf и остальные требования профиля токена. Нельзя выбирать доверенный источник ключей по произвольному адресу из непроверенного токена или принимать любой алгоритм из его заголовка. Рекомендации по JWT, RFC 8725.

JWT не обязан быть access token, а access token не обязан быть JWT. Короткий срок жизни уменьшает время возможного злоупотребления, но не решает вопрос немедленного отзыва. Сроки, ротацию refresh token, выход из сессии и реакцию на компрометацию проектируют вместе.

OAuth 2.0 и OpenID Connect

OAuth 2.0 предоставляет механизм ограниченного доступа приложения к ресурсам. Для входа пользователя поверх OAuth обычно применяют OpenID Connect: он определяет идентификационный слой и ID Token. ID Token предназначен клиенту для проверки входа и не должен автоматически использоваться вместо access token для произвольного API. OpenID Connect Core.

В современной схеме Authorization Code с PKCE клиент создаёт одноразовый code_verifier и соответствующий code_challenge. После аутентификации и согласия сервер авторизации возвращает code на зарегистрированный адрес. Клиент обменивает code на токены, передавая verifier и, если требуется для его типа, свою аутентификацию.

Публичное SPA или мобильное приложение не может хранить общий client_secret как настоящий секрет. PKCE обязателен для публичных клиентов и рекомендован для конфиденциальных. Проверяйте адрес возврата и привязку ответа к начатому запросу; state и предусмотренный OIDC nonce решают соответствующие задачи защиты. Предпочитайте Authorization Code, избегайте implicit flow; password grant в текущем BCP запрещён. OAuth 2.0 Security BCP, RFC 9700.

Не реализуйте проверку протокола вручную по короткой схеме из статьи. Используйте библиотеку для выбранного клиента и сервера авторизации, задайте минимальные scope и проверьте обработку отмены, ошибки, повторного code и истёкшей сессии.

Cookie, Web Storage, CSRF и XSS

localStorage и sessionStorage доступны JavaScript того же origin. Внедрённый через XSS скрипт может прочитать находящийся там токен. Cookie с HttpOnly не читается JavaScript, но XSS всё ещё может выполнять действия от имени пользователя в его браузере. Способ хранения выбирают вместе с архитектурой сессии, а не по правилу «один вариант всегда безопасен». OWASP: управление сессиями.

CSRF связан с тем, что браузер автоматически прикладывает учётные данные, например cookie, к запросу. Это не особенность только POST. Если GET изменяет данные, он тоже может создать проблему. Используйте встроенную CSRF-защиту фреймворка и проверку для запросов, меняющих состояние; настройте Secure, HttpOnly и подходящий SameSite. SameSite полезен как дополнительный слой, но не заменяет полноценную защиту во всех архитектурах. OWASP: CSRF.

CORS управляет доступом браузерного JavaScript к ответам между origin. Это не авторизация API и не защита от прямых запросов с сервера или командной строки. OWASP: безопасность REST API.

Для XSS нужны контекстное экранирование вывода, безопасные способы вставки данных и, при необходимости разрешить HTML, подходящая очистка. CSP дополняет эти меры. OWASP: XSS.

Читай также:  Data-driven подход: примеры решений на основе данных

SQL: параметры и права проверяются отдельно

Значения передают в запрос отдельно от SQL-кода. ORM помогает только при корректном использовании: склейка строк внутри raw SQL остаётся опасной. Для динамических имён таблиц, столбцов и направления сортировки обычно нужен заранее разрешённый набор, поскольку параметр значения не заменяет SQL-идентификатор. OWASP: предотвращение SQL-инъекций.

Ниже собственный учебный пример Python/SQLite. База создаётся только в памяти. Параметры защищают структуру запроса, а условие owner_id ограничивает доступ к записи. В реальном приложении verified_user_id должен поступать из проверенной сессии или токена.

import sqlite3

db = sqlite3.connect(":memory:")
db.executescript("""
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    owner_id INTEGER NOT NULL,
    total INTEGER NOT NULL
);
INSERT INTO orders VALUES (42, 7, 1200), (43, 8, 2100);
""")

def read_order(order_id, verified_user_id):
    return db.execute(
        "SELECT id, total FROM orders WHERE id = ? AND owner_id = ?",
        (order_id, verified_user_id),
    ).fetchone()

assert read_order(42, 7) == (42, 1200)
assert read_order(42, 8) is None
assert read_order("42 OR 1=1", 7) is None
print("Проверки пройдены")
db.close()

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

Пароли, файлы и секреты

Пароли пользователей для проверки входа обычно хранят как адаптивные хеши с солью, а не в открытом виде или с обратимым шифрованием. OWASP рекомендует Argon2id; параметры подбирают по актуальным рекомендациям и доступным ресурсам. Обычный быстрый SHA-256 сам по себе не подходит для хранения паролей. OWASP: хранение паролей.

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

При загрузке файлов проверяйте размер, допустимый формат и фактическое содержимое, назначайте серверное имя и ограничивайте путь. Пользовательский Content-Type не является доказательством типа файла. По возможности храните загрузки вне публичного каталога и выдавайте их через проверку доступа; в любом случае исключайте выполнение загруженного кода. OWASP: загрузка файлов.

Права и настройка серверной среды

Универсальной безопасной команды chmod для любого сайта нет. Результат зависит от владельца, группы, ACL, модели PHP-FPM и задач процесса. Например, конфигурацию, которую читает PHP, должен иметь возможность прочитать пользователь этого PHP-процесса; запрет HTTP-доступа к файлу — отдельная настройка веб-сервера.

Разделяйте пользователей приложений, давайте запись только туда, где она нужна, ограничивайте сетевой доступ к базам и административным интерфейсам. Не исправляйте ошибку доступа массовым chmod -R 777 и не меняйте владельца всего проекта без понимания схемы развёртывания.

Поддерживайте ОС, веб-сервер, СУБД и зависимости; проверяйте обновление и восстановление. На рабочем сайте ошибки не должны раскрывать посетителю секреты и внутренние пути. Логи должны помогать диагностике, но не содержать полные пароли, токены и чувствительные тела запросов.

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

Что проверить перед запуском API

  • Клиент проверяет сертификат, секреты не передаются в открытом HTTP.
  • Каждая операция проверяет не только токен, но и права на объект и действие.
  • Повторный запрос после тайм-аута не создаёт нежелательный дубль.
  • SQL использует параметры, вывод учитывает контекст, загрузки не исполняются.
  • Сессии и OAuth/OIDC реализованы поддерживаемыми библиотеками с проверкой ошибок и сроков.
  • Есть ограничения объёма запросов, времени обработки и частоты действий, а также мониторинг и восстановление.

Такой контроль связывает защиту с реальными операциями приложения. Установка HTTPS или выпуск JWT — только часть этой работы.