Сотрудник вошёл в систему — это ещё не значит, что он может открыть любую заявку. В учебном приложении отдела кадров работник создаёт заявку на компенсацию, видит только свои записи, а руководитель команды видит записи сотрудников своего подразделения. Вход подтверждает личность; проверка прав отвечает, какое действие этой личности разрешено.
Authentication: кто отправляет запрос?
После успешного входа сервер создаёт сессию и связывает её с пользователем. Следующий запрос содержит идентификатор сессии — обычно через cookie — и сервер восстанавливает user_id из своего хранилища. Секретную сессию нельзя заменять на JSON в localStorage, который пользователь сам может переписать. OWASP Session Management Cheat Sheet описывает, что HTTP-запросы независимы, поэтому сессия связывает пользователя между запросами; токен сессии эквивалентен временному доказательству входа и должен обрабатываться как секрет.
После изменения уровня привилегий — например, входа или перехода в роль администратора — ID сессии следует обновить; истечение и отзыв нужно проверять на сервере. Конкретные атрибуты cookie, SSO и MFA зависят от выбранного auth-провайдера и архитектуры: не копируй из примера несверенные настройки своего стека.
CSRF для изменяющих запросов с cookie-сессией
Если браузер автоматически прикладывает cookie к запросу, защита от CSRF становится отдельной частью authorization-маршрута: атакующая страница может попытаться заставить уже вошедший браузер отправить изменение. Для POST, PATCH и DELETE выбери защиту, подходящую архитектуре: синхронизирующий CSRF-токен или подписанный double-submit token, проверку Origin (а при необходимости Referer) на доверенный origin и cookie-атрибуты SameSite как дополнительный слой. Не считай SameSite единственной защитой и не применяй Origin-проверку к запросам без ясного списка доверенных origins. OWASP описывает эти варианты и их ограничения в Cross-Site Request Forgery Prevention Cheat Sheet.
Проверка для HR-заявки: отправь изменяющий запрос без CSRF-токена — сервер отклоняет его и запись не меняется; затем повтори с Origin чужого сайта — тот же ожидаемый отказ; запрос с действующей сессией, корректным токеном и доверенным origin проходит обычную проверку прав. Это иллюстративная матрица приёмки: в реальном приложении проверь фактическую конфигурацию браузера, proxy и framework.
Authorization: что можно сделать с этой заявкой?
Условная модель:
| Кто | Читает заявку | Создаёт свою | Меняет статус |
|---|---|---|---|
| Анонимный посетитель | Нет | Нет | Нет |
| Сотрудник | Только собственную | Да | Только черновик своей заявки |
| Руководитель | Заявки своего подразделения | Да | Согласовать или вернуть |
| HR-администратор | По своей служебной роли | Да | Обработать заявку в своей области |
Эта матрица должна стать проверяемой политикой. Для каждого запроса сервер берёт пользователя из подтверждённой сессии, извлекает запись из базы, сверяет owner/department и только затем отдаёт её или изменяет. Не доверяй user_id, role или department_id, которые пришли из формы. OWASP отдельно подчёркивает: авторизация отличается от аутентификации и должна выполняться на каждом запросе для конкретного объекта; контроль доступа размещается на сервере/шлюзе/serverless функции (Authorization Cheat Sheet).
Скрыть кнопку «Согласовать» — полезное поведение интерфейса, но это не защита. Пользователь может отправить запрос напрямую, изменить ID в URL, повторить старый запрос или вызвать endpoint из консоли. Если база поддерживает политики строк, можно добавить дополнительный уровень ограничения. В PostgreSQL при включённой Row Level Security доступ к строкам должен разрешаться политиками; если политика не существует, действует default deny, но владелец таблицы обычно имеет отдельные привилегии (PostgreSQL RLS). Настройка роли подключения и тестов всё равно критична.
Как превратить правило в проверку
Например, у заявки R-410 владелец user-18, подразделение design. Проверь четыре запроса к одному и тому же endpoint:
user-18получаетR-410и видит свои поля;user-21из другого отдела запрашиваетR-410напрямую и получает отказ, не содержимое;- руководитель
designполучает только заявки своего подразделения; - анонимный запрос без активной сессии не может изменить статус, даже если тело содержит
{ "role": "hr_admin" }.
Добавь и отказные случаи: истёкшая или отозванная сессия; подмена ID записи; попытка назначить себе роль; повторный POST после истечения сессии; доступ к вложению через прямой URL. Для каждого случая сохраняй ожидаемый код ответа и убедись, что запись не изменилась. При отказе backend завершает операцию безопасно: UI показывает понятное сообщение, но не исправляет запрет повторной записью.
Сценарий с руководителем сложнее, чем простое сравнение owner_id: принадлежность сотрудника подразделению и полномочия согласующего также должны приходить из достоверных серверных данных. Если временно оставляешь в прототипе только сотрудника и собственные заявки, явно убери интерфейс управления чужими обращениями.
Попроси генератор сначала описать источник user identity, жизненный цикл сессии, матрицу действий и серверные точки проверки. Затем поручай сделать по одному endpoint и тесту на разрешённое и запрещённое действие. Не проси «добавить безопасную авторизацию» как расплывчатый единый шаг. Структура сущностей и внешние ключи подробно разобраны в схеме БД; различие клиентского и серверного слоя — в руководстве по веб-приложению.
