Куда на самом деле ставить проверку прав: разбор на живом примере
Типичная картина на аудите: в роутинге стоит проверка «пользователь авторизован, пускаем». Дальше по коду все обработчики верят, что раз запрос дошел, значит все в порядке. Пока приложение маленькое, это даже работает.
Проблема появляется вместе со вторым способом дозвониться до данных: фоновая задача, вебхук, соседний эндпоинт, который писали в пятницу. Любой из них обходит калитку в роутинге, и данные внезапно доступны без проверки.
На пентестах мы находили такое почти в каждом втором проекте: меняешь числовой id в запросе и получаешь чужой заказ. Роутинг тут ни при чем, он честно проверил логин. Просто никто не спросил, чей это заказ.
Рабочее правило простое: проверка прав живет там, где происходит доступ к данным. В запросе к базе, в сервисном слое, в RLS-политике Postgres, где угодно, но рядом с данными. Роутинг может проверять вход, но он не последняя линия.
В наших проектах это часть обычной сдачи: каждый запрос к данным отвечает на вопрос «а этот пользователь вообще имеет право это видеть». Скучно, зато в отчете пентестера потом пусто.