Кальтер / Блог

Авторизация в приложении, созданном ИИ: роли, сессии и доступ к данным

На примере заявок на компенсацию разберись, чем вход в аккаунт отличается от доступа к конкретной записи, как связывать запрос с сессией и тестировать отказ на сервере.

Материал подготовлен с помощью ИИ; источники и расчёты проверены редакцией ZASLON.

Доступ к данным вычисляется на сервере по проверенной сессии и роли; на схеме: Пользователь, Сессия, Роль.
Редакционная схема ZASLON. Условная иллюстрация механизма; не рыночные данные и не интерфейс сервиса.

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

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 и тесту на разрешённое и запрещённое действие. Не проси «добавить безопасную авторизацию» как расплывчатый единый шаг. Структура сущностей и внешние ключи подробно разобраны в схеме БД; различие клиентского и серверного слоя — в руководстве по веб-приложению.