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

Один из самых дорогих дефектов в API выглядит безобидно: сервер проверяет, что пользователь вошёл в систему, но не проверяет, имеет ли он право читать именно этот заказ, документ или объект. В результате чужие данные открываются простой заменой идентификатора в URL. Ни OAuth 2.0, ни JWT сами по себе не решают эту проблему: они подтверждают личность или выдают полномочия, но не принимают бизнес-решение за ваш сервис.
Где проходит граница между входом и доступом
У API есть как минимум два разных вопроса:
- Аутентификация — кто отправил запрос. Например, сервер проверил access token и получил идентификатор пользователя.
- Авторизация — что этому пользователю разрешено сделать с конкретным ресурсом. Например, можно ли ему изменить заказ конкретного магазина.
Типичная ошибка — считать ответ «токен действителен» достаточным. Валидный токен говорит, что запрос связан с определённым субъектом, но не доказывает право на каждый объект, метод и поле ответа.
Самая опасная проверка доступа — та, которую разработчик мысленно считает очевидной и поэтому не записывает в коде и тестах.
[1] BOLA (Broken Object Level Authorization) — уязвимость, при которой API разрешает обращаться к чужому объекту, потому что проверяет вход в систему, но не связь объекта с текущим пользователем или его ролью.
Пять ошибок, которые повторяются в API
1. Доверять идентификатору из URL
Запрос вроде GET /api/orders/4815 содержит идентификатор, который контролирует клиент. Нельзя считать, что раз пользователь знает номер заказа, значит он имеет к нему доступ. Сервер должен получить текущего субъекта из проверенного контекста запроса, найти заказ и отдельно проверить владельца, организацию или иное правило доступа.
Проверка должна выполняться для чтения, изменения, удаления и скачивания файла. Особенно часто забывают про отдельный endpoint экспорта: карточка заказа защищена, а CSV с теми же данными — нет.
2. Считать роль единственным правилом
Проверка «у пользователя роль manager» слишком груба для многотенантной системы. Менеджер компании A не должен видеть документы компании B только потому, что у него та же роль. Сначала ограничьте выборку организацией или другим контекстом арендатора, затем применяйте роль и дополнительные условия.
Права могут зависеть от нескольких факторов: владелец объекта, подразделение, регион, статус документа, время операции и тип действия. Если такие правила разбросаны по контроллерам, через несколько месяцев разные endpoints начинают принимать противоречивые решения.
3. Выдавать широкие scopes «на всякий случай»
Scope должен описывать минимально нужное полномочие. orders:read безопаснее, чем условный admin, если интеграции требуется только список заказов. Но scope не заменяет проверку конкретного объекта: право читать заказы не означает право читать заказы всех организаций.
Для сервисных интеграций заранее разделяйте доступ к чтению, изменению и административным операциям. Отдельные клиенты и учётные записи упрощают отзыв доступа и расследование инцидента.
4. Неправильно проверять JWT
JWT нужно проверять не только на наличие и срок действия. Сервер должен принимать только ожидаемый алгоритм и доверенного издателя, проверять аудиторию, время жизни и необходимые claims. Подпись подтверждает целостность токена, но не делает его бессрочным и не отзывает уже выданные полномочия.
Не принимайте алгоритм, указанный в токене, как готовую настройку доверия. Алгоритм, ключи и допустимые значения должны задаваться конфигурацией сервера и меняться по процедуре ротации. Если конкретная библиотека предлагает несколько режимов валидации, сверяйте их с документацией именно установленной версии, а не с примером из старого блога.
5. Считать CORS механизмом защиты API
CORS ограничивает, какие браузерные страницы могут читать ответы, но не превращает endpoint в закрытый. Запрос можно отправить не из браузера, а через скрипт, мобильное приложение или другой сервер. Авторизация должна работать независимо от CORS.
Опасная комбинация — разрешить произвольные источники, включить credentials и использовать cookie-сессию без продуманной защиты от межсайтовых запросов. Список доверенных origins должен быть явным, а тесты — проверять как разрешённые, так и запрещённые источники.
Сравнение подходов к выдаче доступа
Выбор механизма зависит от клиента, срока жизни сессии и требования к отзыву доступа. Ниже — не рейтинг, а карта компромиссов.
| Подход | Что выигрываете | За что платите | Где уместен |
|---|---|---|---|
| Серверная сессия и HttpOnly cookie | Простой отзыв сессии, токен не виден JavaScript | Нужно хранить состояние или общий слой сессий; требуется защита от CSRF | Веб-приложение с собственным браузерным интерфейсом |
| Короткоживущий bearer access token | Удобен для мобильных клиентов и сервисов, легко передаётся между API | Украденный токен действует до истечения срока; нужен refresh и отзыв | Распределённые API и внешние интеграции |
| JWT с локальной проверкой | Сервис может проверить токен без запроса к центральному хранилищу | Сложнее ротация ключей и отзыв; claims могут устареть | Системы, где допустима короткая задержка отзыва |
| Opaque token с проверкой у сервера авторизации | Централизованный отзыв и актуальные права | Дополнительный сетевой запрос и зависимость от сервера авторизации | Критичные операции и строгий контроль сессий |
Независимо от варианта не помещайте секреты в URL: адреса попадают в историю, прокси, аналитику и журналы. Для cookie и bearer-токенов нужны разные меры защиты, поэтому не переносите настройки одного механизма на другой без проверки.
Как проектировать проверку доступа
- Опишите ресурс. Для каждого endpoint укажите объект, действие и владельца: «изменить счёт организации», а не просто «вызвать PUT». Это выявляет скрытые различия между похожими маршрутами.
- Сформулируйте правило рядом с моделью данных. Например: пользователь может читать документ, если он состоит в организации документа и имеет право на этот тип документа. Не подменяйте это условием «роль не равна guest».
- Разделите область данных и действие. Сначала ограничьте запросы текущим tenant или владельцем, затем проверьте действие, статус и роль. Если проверка сделана после загрузки объекта, убедитесь, что объект не утекает через ошибки, кэш или побочные события.
- Закройте массовое присваивание. Не принимайте из тела запроса поля вроде
owner_id,tenant_id,roleиis_adminкак обычные пользовательские атрибуты. Эти значения должен назначать сервер по отдельному правилу. - Проверяйте негативные сценарии. Тестируйте не только успешный запрос владельца, но и доступ к чужому объекту, объекту другой организации, удалённому объекту и объекту после смены роли. В каждом случае должен быть предсказуемый отказ без лишних подробностей.
- Логируйте решение, а не секрет. Записывайте идентификатор субъекта, endpoint, объект, результат проверки и корреляционный идентификатор. Access token, cookie и персональные данные в логах оставлять нельзя.
Как проверять систему на практике
Начните с матрицы доступа. В строках разместите роли или типы клиентов, в столбцах — операции и ресурсы. Для многотенантной системы добавьте минимум два tenant: положительный тест из своей области и отрицательный тест из чужой. Такая матрица быстрее обнаруживает пробелы, чем ручной просмотр нескольких контроллеров.
Отдельно проверьте разные способы получить один и тот же объект: основной JSON endpoint, поиск, экспорт, вложенный ресурс, скачивание файла, WebSocket или webhook. Уязвимость часто появляется не в основном маршруте, а в менее популярном представлении данных.
Для каждого отказа определите единое поведение. Отсутствие прав не должно превращаться в ответ с трассировкой, SQL-фрагментом или существованием чужого объекта, если это раскрывает лишнюю информацию. Конкретный статус и формат ошибки выбирайте с учётом контракта API и модели угроз, но не смешивайте «не вошёл» и «вошёл, но права нет» без причины.
Что ломается первым в проде
Отзыв доступа не действует мгновенно
Если роли и организации лежат внутри долгоживущего JWT, смена прав не повлияет на уже выданный токен. Для чувствительных операций используйте короткое время жизни, проверку актуального состояния или отдельный механизм отзыва. Это компромисс между задержкой запроса, доступностью центра авторизации и скоростью блокировки.
Кэш смешивает пользователей
Ответ, зависящий от пользователя, организации или scope, нельзя кэшировать как общий ресурс без правильного разделения ключей и заголовков. Ошибка на этом уровне превращает корректную авторизацию в утечку из кэша. Проверяйте не только origin-сервер, но и CDN, reverse proxy и внутренние кэши.
Сервисы доверяют заголовкам от клиента
Заголовки вроде идентификатора пользователя или организации должны считаться достоверными только после того, как их установил доверенный шлюз и следующий сервис проверил границу доверия. Клиентский запрос не должен сам назначать себе tenant через дополнительный header.
Что делать дальше
Составьте инвентаризацию endpoint и для каждого зафиксируйте субъект, ресурс, действие, область данных и ожидаемый отказ. Затем добавьте негативные тесты для BOLA, массового присваивания, просроченных токенов и смены организации. В проде следите за долей отказов по endpoint, необычными переходами между tenant, массовыми выгрузками и обращениями к объектам, которые пользователю не принадлежат. Инвалидация кэша и ротация ключей — отдельные темы, их стоит разобрать после базовой матрицы доступа.
Проверка рекламы на риски: Что в макете вызовет вопросы ФАС: превосходные степени, сравнения, обещания результата, маркировка.
Спросить за 40 ₽Источники: RFC 9700: Best Current Practice for OAuth 2.0 Security, OWASP API Security Top 10 — 2023
Частые вопросы
Достаточно ли JWT, чтобы защитить API?
Нужно ли проверять права на каждом endpoint?
Что выбрать: JWT или серверные сессии?
Что делать, если пользователь уже получил чужой объект?
Можно ли считать CORS защитой от вызовов API?
Что изменится, если у меня другая версия библиотеки авторизации?
Какие подводные камни важнее всего в проде?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.