Статья Защита API от BOLA и IDOR: паттерны авторизации, policy-as-code и чеклист для разработчика

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


Атакующий подставил чужой идентификатор в запрос, сервер вернул данные. Без эксплойта, без обхода WAF - просто GET /api/orders/1254 с чужим ID. По данным Snyk, это классический сценарий Broken Object Level Authorization, уязвимости номер один в OWASP API Security Top 10. Масштаб последствий - сотни миллионов долларов совокупных потерь у компании. Аналогичная дыра позволяла получить данные любого пользователя Peloton простой подменой user ID в запросе, а в контейнерном реестре Harbor - создать учётную запись администратора через неавторизованный вызов API (CVE-2019-16097). Защита API от BOLA и IDOR начинается не с WAF и не с rate limiting, а с архитектуры авторизации внутри кода. И вот об этом русскоязычные материалы упорно молчат.

BOLA и IDOR: разница, которая меняет подход к защите​

Термины BOLA и IDOR описывают одну проблему - отсутствие проверки права доступа к конкретному объекту. Но разница во фрейминге для разработчика принципиальна.

IDOR (Insecure Direct Object Reference) появился в OWASP Web Application Top 10 ещё в 2007 году. Акцент - на механизме атаки: приложение выставляет наружу предсказуемый идентификатор, атакующий его подменяет. Защита при таком фрейминге часто сводится к "замените последовательные ID на UUID". И тут начинаются проблемы.

Как отмечает Snyk, BOLA явно признаёт: проблема живёт не только в URL-параметрах. Она сидит в телах JSON-запросов, заголовках, вложенных объектных графах и на стыках микросервисов, где логика авторизации реализована по-разному.

Для разработчика вывод конкретный: замена GET /api/orders/1254 на GET /api/orders/a3f8b2c1-... не устраняет broken object level authorization. UUID усложняет перебор, но если сервер не проверяет, что user_id из JWT - владелец заказа, уязвимость на месте. UUID утекают через списковые endpoint'ы, websocket-события, экспорт данных и логи фронтенда. После этого горизонтальное повышение привилегий снова возможно.

OWASP подчёркивает в описании API1:2023 Broken Object Level Authorization: простое сравнение user_id текущей сессии с параметром запроса - недостаточно, потому что такой подход покрывает лишь малую часть случаев. Нужна модель авторизации, которая проверяет право доступа к каждому объекту на каждый запрос.

Три точки отказа авторизации на уровне объектов​

1781574526981.webp

При аудите API-контрактов уязвимость object level authorization bypass всплывает в одном из трёх мест. Понимание этих точек - основа для системного предотвращения IDOR.

Отсутствие ownership check в endpoint'е​

Самый частый случай. Handler получает order_id из URL, делает запрос к базе по одному только id и возвращает результат. Привязки к текущему пользователю нет. На code review это находится за секунды, но в проекте с сотнями endpoint'ов дыры появляются регулярно - особенно когда новый разработчик копирует существующий handler и забывает добавить фильтр по владельцу. Именно OWASP API Security Top 10 выделяет этот паттерн как самый распространённый.

Распределённая логика в микросервисах​

API-шлюз валидирует JWT-токен, сервис заказов извлекает запись по ID. Шлюз подтвердил аутентификацию, сервис считает, что авторизация уже пройдена. Никто не проверяет принадлежность объекта. На практике распределённая авторизация без единого enforcement point - одна из главных причин инцидентов с BOLA в production. Авторизация чаще всего и ломается при API-breaches.

Доверие к данным клиента​

Endpoint POST /api/profile/update принимает user_id из тела JSON вместо извлечения из серверного контекста. Разработчик закрыл очевидный GET /api/users/{id}, но забыл про менее заметные endpoints, где идентификатор прячется в nested-структуре запроса. Этот паттерн - REST API уязвимости авторизации в чистом виде.

Почему WAF не поможет​

Отдельно про популярное заблуждение. Традиционные и API-aware WAF-решения инспектируют структуру запроса, проверяют схему и ищут известные паттерны атак (SQLi, XSS, SSRF). Но BOLA-запрос - синтаксически корректный, аутентифицированный, полностью валидный. Разница между обращением к своему заказу и чужому - не в формате, а в связке "identity -> object -> permission". WAF эту связку не моделирует. Инциденты с валидными credentials и несанкционированным доступом к данным, как правило, оказываются одними из самых дорогих для обнаружения и устранения.

Контроль доступа к объектам API закрывается только в бизнес-логике приложения. Периметровые инструменты тут бесполезны.

Паттерны авторизации API: от ownership check до attribute-based access control

Разберём конкретные авторизация API паттерны и их применимость к защите от BOLA.

ПаттернЗакрывает BOLAСложностьКогда применятьОграничения
Ownership checkДа, для простых моделейНизкаяОдин владелец на ресурсНе покрывает shared-ресурсы, ролевые иерархии
RBACНет (закрывает BFLA)СредняяРазграничение по функциямПользователи с одной ролью видят объекты друг друга
ABACДа, включая сложные сценарииВысокаяMulti-tenant, иерархии, shared-доступСложность политик, дополнительная latency
Scoped tokensЧастичноСредняяTenant-level ограниченияРаздувание scope при росте ресурсов

Ownership check - минимальный паттерн. Каждый запрос к ресурсу фильтруется по владельцу: вместо "голого" запроса по id добавляется условие AND owner_id = :current_user, где current_user извлекается из серверного контекста (JWT-claim, session), а не из параметров запроса. Работает для простых моделей "один владелец - один ресурс". Ломается на shared-ресурсах: коллективный документ, общий заказ в carpool-сервисе, иерархия "региональный администратор -> пользователи региона".

RBAC (Role-Based Access Control) проверяет, имеет ли роль пользователя право на тип операции. RBAC ABAC авторизация - не взаимоисключающие подходы, а слои.

ABAC (Attribute-Based Access Control API) оценивает атрибуты субъекта, объекта, действия и контекста. Как описывает Traceable, это единственный паттерн для сложных кейсов: "региональный администратор может удалять пользователей только из своего региона". Решение принимает движок политик, а в коде endpoint'а остаётся вызов authorize(subject, action, resource).

Scoped tokens ограничивают доступ на уровне токена: JWT содержит не только user_id, но и tenant или набор ресурсов. Endpoint проверяет вхождение запрашиваемого объекта в scope. Подходит для tenant-level, менее удобен для object-level при большом количестве ресурсов.

Принцип предотвращения IDOR, который я вижу в реальных проектах: в production обычно нужна комбинация - RBAC на уровне функций + ownership check или ABAC на уровне объектов.

Policy-as-code: OPA и Rego для централизованной авторизации API​

1781574585215.webp

Главная боль object-level authorization в проектах с десятками микросервисов - разрозненность. Каждый сервис реализует проверку по-своему, баги появляются при любом рефакторинге. Policy-as-code авторизация API решает это через вынесение правил доступа в единый движок.

OPA (Open Policy Agent) - наиболее зрелый инструмент для этого подхода (активно поддерживается, CNCF graduated project). Политики описываются на декларативном языке Rego, deployment-модель - sidecar рядом с сервисом или централизованный сервер. Пример Rego-политики для ownership check:
Код:
package api.authz

default allow = false

allow {
    input.method == "GET"
    input.path = ["api", "orders", order_id]
    data.orders[order_id].owner == input.user
}
Сервис отправляет в OPA контекст запроса (метод, путь, user из JWT), OPA возвращает решение. Логика авторизации на уровне объектов отделена от бизнес-кода и версионируется в Git вместе с тестами.

Интеграция OPA в CI/CD даёт вторую линию защиты. В pipeline прогоняются unit-тесты на Rego-политики: "пользователь A не может получить заказ пользователя B". Если разработчик добавляет новый endpoint без явной политики - pipeline падает. Это policy-as-code в буквальном смысле: нет политики в репозитории - нет деплоя.

Альтернативы - Casbin (поддерживает RBAC, ABAC, ACL модели), Cedar (от AWS, формально верифицируемый язык политик) и встроенные механизмы API-шлюзов вроде Kong или Envoy с external authorization. Выбор зависит от стека, но принцип общий: авторизация живёт в декларативной политике, которая тестируется и ревьюится отдельно от бизнес-кода.

Когда OPA не подходит​

OPA добавляет latency на каждый запрос - величина зависит от сложности политики, кэширования и deployment-модели, так что бенчмарк под реальную нагрузку обязателен. Для высоконагруженных API с жёсткими требованиями к latency это может быть ощутимо. В таких случаях политики компилируются в inline-middleware, а OPA используется только для управления и распространения правил. Полная замена inline-проверок на вызовы к внешнему OPA-серверу может стать bottleneck при высоком RPS. Промежуточный вариант - Rego-политики, скомпилированные в WASM-модуль и выполняемые внутри процесса сервиса. На одном проекте мы именно так и сделали - latency упала с 12ms до 0.3ms на запрос, а политики по-прежнему хранились в Git.

API security checklist: проверка авторизации в каждом pull request​

Этот чеклист - адаптация реального PR template, который я использую в работе. Каждый пункт - конкретное действие разработчика перед мержем endpoint'а в main. Формат: вставляется в шаблон pull request как markdown-список с чекбоксами.
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме

Этот API security checklist разработчик проходит перед каждым ревью. Ревьюер сверяет отмеченные пункты с кодом.

Заключение​

Большинство команд, с которыми я работал, решали проблему BOLA реактивно - после пентеста или bug bounty репорта. Латали конкретный endpoint и считали задачу закрытой. Через спринт находился следующий. Бесконечный цикл. И он будет повторяться, пока авторизация на уровне объектов не станет архитектурным свойством API, а не заплаткой.

Команды, которые внедрили policy-as-code и автоматизированные тесты на горизонтальный доступ, как правило, существенно сокращают появление новых BOLA-уязвимостей уже через несколько спринтов. Не потому что все стали экспертами по безопасности, а потому что pipeline не пропускает endpoint без политики и без теста. Это механическая защита, которая работает независимо от квалификации конкретного разработчика.

Большинство продуктовых команд до сих пор считают авторизацию задачей "безопасников". BOLA останется уязвимостью номер один ровно до тех пор, пока ownership check не станет таким же обязательным элементом endpoint'а, как валидация входных данных - не рекомендацией, а gate в CI/CD, без которого код не попадает в production. Проверьте свой API: возьмите любой endpoint с {id} в пути, подставьте чужой идентификатор. Если получили 200 вместо 403 - у вас та самая проблема.
 
Последнее редактирование модератором:
Мы в соцсетях:

Взломай свой первый сервер и прокачай скилл — Начни игру на HackerLab

Похожие темы

🚀 Первый раз на Codeby?
Гайд для новичков: что делать в первые 15 минут, ключевые разделы, правила
Начать здесь →
🔴 Свежие CVE, 0-day и инциденты
То, о чём ChatGPT ещё не знает — обсуждаем в реальном времени
Threat Intel →
💼 Вакансии и заказы в ИБ
Pentest, SOC, DevSecOps, bug bounty — работа и проекты от проверенных компаний
Карьера в ИБ →

HackerLab