securityowaspnonfunctionalaccess-controlqa

Broken Access Control: как QA тестировать права доступа (топ-1 риск OWASP)

Самый частый способ увидеть чужие данные — не хитрый взлом, а замена одной цифры в URL. Открываешь свой заказ по адресу /orders/1043, меняешь на /orders/1044 — и видишь чужой заказ с именем, адресом и телефоном. Это не выдуманный сценарий: сломанный контроль доступа стоит на первом месте в OWASP Top 10, и что приятно для нас — большую часть таких дыр QA ловит обычными руками, без пентестера и спецтулов. Нужно только знать, куда тыкать.

Контроль доступа отвечает на вопрос «этому пользователю можно делать это действие с этим объектом?». Ломается он там, где проверку либо забыли, либо сделали только на клиенте. Разберём по типам.

IDOR — подставь чужой идентификатор

IDOR (Insecure Direct Object Reference) — когда объект достаётся по идентификатору из запроса, а сервер не проверяет, твой ли это объект. Классика: id в URL, в теле запроса, в скрытом поле формы, в куке.

Как тестировать: заведи двух пользователей (A и B). Под A открой свой ресурс — заказ, документ, профиль, файл — и запомни его id. Теперь под сессией B (или вообще без авторизации) запроси этот же id. Должно быть 403/404, а не чужие данные. Проверяй не только цифровые id — GUID и хэши тоже не защита, если их можно узнать (в письме, в логах, в чужой ссылке).

Где искать: скачивание файлов (/download?file=...), просмотр заказа/брони/тикета, API вида /api/users/{id}, экспорт, инвойсы, аватары, вложения.

Горизонтальная и вертикальная эскалация

Две разные вещи, проверять надо обе:

  • Горизонтальная — доступ к данным пользователя того же уровня. Ты обычный юзер и лезешь в данные другого обычного юзера (тот самый IDOR по сути). «Я вижу чужой профиль/заказ».
  • Вертикальная — обычный пользователь выполняет действие уровнем выше. «Я обычный юзер, но дёргаю админский эндпоинт и меняю чужую роль / удаляю пользователя / вижу админку». Это опаснее.

Тест вертикальной: залогинься обычным юзером, найди (по докам, по трафику админа, по угадыванию) админское действие и повтори его своей сессией. Права должны резаться на сервере, а не тем, что «в меню кнопки нет».

Forced browsing — кнопки нет, а ручка работает

Отсутствие ссылки в интерфейсе — это не защита. Прямой заход по адресу — базовый тест:

  • Открой /admin, /settings/users, /reports напрямую, будучи обычным юзером или гостем.
  • Дёрни API-эндпоинт, который UI показывает только админам.
  • Зайди на «страницу успеха» или шаг мастера в обход предыдущих шагов.

Если контент отдаётся — доступ битый, даже если «в интерфейсе туда не попасть».

Проверяй на бэке, а не в UI

Главная ловушка QA: проверить, что кнопка «Удалить» спрятана у обычного юзера, и на этом успокоиться. Спрятать элемент ≠ закрыть доступ. UI-ограничения — это удобство, а не безопасность. Настоящая проверка — дёрнуть сам запрос:

  • Повтори действие напрямую (через DevTools → Network → «Copy as fetch/cURL», Postman, Proxyman/Charles).
  • Убери/поменяй параметр, который «должен» ограничивать (role=userrole=admin, isAdmin=falsetrue).
  • Отправь запрос, которого в твоём UI нет вообще, но он есть у роли выше.

Если сервер выполнил — дыра, независимо от того, что показывал интерфейс.

Токены, куки и роли

  • Чужой/старый токен: подставь токен другого пользователя — доступ должен быть закрыт. Протухший или отозванный токен не должен работать (проверь logout и смену пароля — старые сессии обязаны инвалидироваться).
  • Смена роли на лету: если роль понизили/забанили, активная сессия должна перестать пускать — а не «до перелогина».
  • Подмена в токене: если это JWT — проверь, что сервер валидирует подпись, а не верит полю role из payload; смена alg на none не должна прокатывать.
  • Границы арендатора (multi-tenant): в B2B проверь, что пользователь одной компании не видит данные другой, подставив чужой tenant_id/org_id.

Матрица ролей — база для тестов

Собери простую таблицу: строки — действия/ресурсы, столбцы — роли (гость, юзер, чужой юзер, менеджер, админ). В клетке — «можно/нельзя». Каждую «нельзя» превращаешь в негативный тест: делаешь запрос из-под этой роли и ждёшь 403. Такая матрица разом покрывает и горизонталь, и вертикаль, и forced browsing — и не даёт забыть роль.

Чек-лист

  • Заведены минимум два пользователя (A и B) + гость для кросс-проверок.
  • IDOR: подстановка чужого id (URL, тело, скрытые поля, куки) → 403/404.
  • GUID/хэш-id тоже проверены (не защита, если утекают).
  • Горизонталь: доступ к данным равного пользователя закрыт.
  • Вертикаль: обычный юзер не выполняет админские действия через API.
  • Forced browsing: прямой заход на /admin и скрытые эндпоинты → отказ.
  • Проверка на бэке: действие повторено запросом в обход UI, не только «кнопка спрятана».
  • Подмена параметров роли/флагов (role, isAdmin) не даёт прав.
  • Чужой/протухший/отозванный токен не работает; logout инвалидирует сессию.
  • JWT: подпись валидируется, alg:none не проходит, role из payload не доверяется.
  • Multi-tenant: изоляция между компаниями/организациями.
  • Есть матрица «роль × действие», каждая «нельзя» покрыта негативным тестом.

Типовые места, где ломается

  • Эндпоинты, добавленные позже основного функционала («быстро прикрутили экспорт»).
  • Массовые операции и batch-API (проверку ставят на один объект, забывают на список).
  • Прямые ссылки на файлы и вложения в обход приложения.
  • «Служебные» и debug-эндпоинты, забытые в проде.
  • GraphQL/агрегирующие запросы, где авторизация на верхнем поле есть, а на вложенном — нет.

Коротко — что забрать с собой

  • Broken Access Control — №1 в OWASP, и QA ловит его руками без пентестера.
  • Главный тест — повторить запрос из-под другой роли/пользователя и ждать 403, а не смотреть на UI.
  • IDOR, горизонталь, вертикаль, forced browsing — четыре обязательных класса проверок.
  • Спрятанная кнопка — не защита; истина на бэке.
  • Матрица «роль × действие» превращает хаос в набор негативных тестов.

Почитать: OWASP — A01 Broken Access Control · OWASP Web Security Testing Guide · OWASP — IDOR Prevention Cheat Sheet · PortSwigger — Access control vulnerabilities