АрхитектураПроектирование системРазработчик серверных приложений

Запрос к API содержит идентификатор ресурса в URL. Как не допустить выдачу чужого ресурса при корректном фо...

Запрос к API содержит идентификатор ресурса в URL. Как не допустить выдачу чужого ресурса при корректном формате запроса?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Нужно проверять не только подлинность пользователя, но и его право доступа именно к ресурсу, указанному в запросе. Сервер должен загрузить ресурс и применить объектную авторизацию по контексту пользователя; одного факта наличия идентификатора или успешной аутентификации недостаточно.

Исторический контекст

Ранние API часто ограничивались проверкой сессии или токена: если запрос исходил от известного пользователя, сервер считал его допустимым. Такой подход не учитывает, к каким конкретным объектам пользователь имеет доступ.

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

Постановка проблемы

Пусть пользователь с идентификатором A отправляет корректный запрос к ресурсу, принадлежащему пользователю B. Проверка формата идентификатора и наличие действующего токена пройдут успешно, но выдача объекта будет нарушением авторизации.

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

Неверное решение приводит к горизонтальному повышению привилегий: пользователь получает доступ к данным другого пользователя без изменения своей роли. Утечка может возникнуть даже при полностью исправной аутентификации.

Подробное решение

Сервер должен выполнять проверку в контексте текущего пользователя и конкретного объекта. Логика обычно состоит из следующих этапов:

  1. Проверить подлинность запроса и получить субъект, от имени которого он выполняется.
  2. Найти ресурс по идентификатору.
  3. Проверить правило доступа к этому ресурсу: владельца, область организации, членство, роль или другое бизнес-условие.
  4. Только после успешной проверки вернуть ресурс или выполнить операцию над ним.

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

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

Проверку нельзя делать только при чтении. Те же правила должны применяться к изменению, удалению, скачиванию, запуску операций и вложенным ресурсам. Особенно важно повторно проверять доступ при фоновой обработке, если задача была создана одним пользователем, а выполняется позже.

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

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

Ситуация из практики

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

Рассматривались два варианта. Первый — фильтровать документы только на уровне клиентского интерфейса; это почти не защищает API и не подходит для прямых запросов. Второй — добавлять проверку прав в каждый обработчик вручную; это лучше защищает данные, но создаёт риск расхождения правил и пропуска проверки в новом endpoint.

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

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

Что кандидаты часто упускают

1. Достаточно ли использовать непредсказуемые идентификаторы вместо проверки прав?

Нет. Непредсказуемый идентификатор усложняет перебор, но не выражает разрешение доступа. Идентификатор может попасть в журнал, сообщение, ссылку или ответ другого сервиса; если сервер не проверяет права, любой такой случай превращается в утечку.

2. Где проверять доступ: до или после загрузки ресурса?

Для проверки, зависящей от свойств ресурса, серверу сначала нужно получить сам объект или необходимые для политики атрибуты. Затем он проверяет право доступа и не возвращает содержимое до успешного результата. В списковых запросах предпочтительнее сразу формировать выборку с ограничением по области доступа, чтобы запрещённые объекты не извлекались без необходимости.

3. Нужно ли повторять проверку в каждом микросервисе?

Каждый сервис, который непосредственно возвращает или изменяет защищённые данные, должен сам обеспечить авторизацию; доверять только проверке во внешнем шлюзе опасно. Шлюз может проверять токен и общие ограничения, но внутренний сервис должен учитывать свои ресурсы и бизнес-правила. Иначе новый маршрут, внутренний вызов или ошибка маршрутизации могут обойти защиту.