Broken Access Control: как не отдать чужой заказ по номеру в ссылке
Первая строчка OWASP - нарушение контроля доступа. Разбираем IDOR и почему проверять права надо на уровне объекта на сервере, а не прятать кнопки в интерфейсе.
Нарушение контроля доступа остаётся первой строчкой рейтинга OWASP, и большинство находок на аудите веб-приложений приходится именно на него
Самый частый и самый недооценённый класс уязвимостей в веб-приложениях - не хитрая инъекция и не переполнение буфера, а банальная возможность открыть чужие данные, просто поменяв число в адресе. В рейтинге OWASP нарушение контроля доступа (Broken Access Control) стоит на первой строчке, и на аудитах мы находим его чаще всего. Разберём, как оно устроено и как его закрывают на этапе проектирования, а не латают потом.
Как это выглядит
Классический сценарий. Пользователь открывает свой заказ по адресу вида /orders/123. Меняет 123 на 124 и видит чужой заказ: имя, телефон, состав, сумму. Приложение проверило, что пользователь вошёл в систему, но не проверило, что заказ 124 принадлежит именно ему.
Это называется небезопасная прямая ссылка на объект (IDOR, Insecure Direct Object Reference). Объект адресуется предсказуемым идентификатором, а проверки владельца при обращении к нему нет. То же самое встречается в адресах API, в скачивании файлов по имени, в параметрах отчётов. Везде, где на сервер приходит идентификатор ресурса, возникает вопрос: а имеет ли право этот пользователь на этот конкретный ресурс.
Опасность в том, что для эксплуатации не нужны инструменты. Достаточно любопытства и браузера. Поэтому такие дыры находят не только на аудите, но и случайные пользователи, а за ними и те, кто ищет их намеренно.
Почему возникает
Корень почти всегда один: проверку прав ставят не на том уровне.
Разработчик проверяет, что пользователь аутентифицирован, то есть вошёл под своей учётной записью. Это проверка «кто ты». Но пропускает проверку авторизации на уровне объекта - «а можно ли тебе именно этот заказ». Аутентификация без авторизации на объекте и даёт IDOR.
Вторая частая причина - расчёт на то, что пользователь не увидит ссылку. В интерфейсе кнопки «чужой заказ» нет, значит, кажется, туда и не попасть. Но адрес формируется на клиенте, и спрятать его в интерфейсе не значит закрыть. Сокрытие в интерфейсе - это не контроль доступа, а его иллюзия.
Как проектировать правильно
Контроль доступа - это не фильтр, который навешивают в конце, а свойство, которое закладывают в архитектуру. Несколько правил, которые реально работают.
Проверять права на сервере при каждом обращении к объекту. Не на входе в раздел, а на каждый конкретный ресурс. Запрос заказа 124 должен сопровождаться проверкой: этот заказ принадлежит текущему пользователю или у него есть роль, дающая доступ. Нет привязки - отказ.
Запрет по умолчанию. Доступ разрешается явно, а не запрещается явно. Новый метод API, про который забыли написать проверку, должен быть по умолчанию закрыт, а не открыт. Это разница между «случайно оставили дыру» и «случайно оставили лишний замок».
Привязка ресурса к владельцу на уровне запроса к данным. Надёжнее всего, когда сама выборка из базы идёт с условием принадлежности: не «дай заказ 124, потом проверим», а «дай заказ 124, только если его владелец - текущий пользователь». Тогда чужой объект просто не вернётся, даже если проверку где-то забыли продублировать.
Непредсказуемые идентификаторы как дополнительный, а не основной барьер. Случайные идентификаторы вместо последовательных чисел усложняют перебор, но это не замена проверке прав. Полагаться только на то, что номер не угадают, нельзя.
Как это ловить
Автоматические сканеры находят IDOR плохо: чтобы понять, что заказ 124 чужой, нужно знать бизнес-смысл, а сканер его не знает. Поэтому основной способ - ручная проверка с двумя учётными записями: заходим под одним пользователем, берём идентификаторы его ресурсов, пытаемся достучаться до них под другим. Такие проверки входят в аудит веб-приложения по методике OWASP.
Помогает и дисциплина на этапе разработки: правило, что ни один метод, отдающий данные по идентификатору, не проходит ревью без явной проверки прав. Частично это ловят и инструменты статического анализа в конвейере сборки, про которые был отдельный разбор, но полностью полагаться на них здесь нельзя.
Итого
Broken Access Control держит первую строчку OWASP не потому, что сложен, а потому, что его легко упустить: аутентификацию делают все, а проверку прав на уровне конкретного объекта - забывают. Лечится это не одним фильтром, а принципом «запрет по умолчанию плюс проверка владельца при каждом обращении», заложенным в архитектуру.
Если не уверены, что ваши приложения не отдают чужие данные по подмене идентификатора, это первое, что стоит проверить на аудите - ручной прогон под двумя учётными записями показывает картину быстро.