АрхитектураАрхитектура безопасностиИнженер по безопасности приложений

Практическая ситуация: шлюз передаёт сервису идентификатор пользователя в заголовке. Как сервис должен дока...

Практическая ситуация: шлюз передаёт сервису идентификатор пользователя в заголовке. Как сервис должен доказать происхождение этой идентичности?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

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

Сервис обязан проверять не только целостность утверждения, но и его актуальность, назначение и полномочия. Сам факт успешной аутентификации шлюза не означает, что любое переданное им значение пользователя корректно.

Исторический контекст

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

Это породило проблему неаутентифицированной передачи идентичности: строка вроде идентификатора пользователя в заголовке выглядит убедительно, но сама по себе не доказывает, кто её установил и не была ли она изменена. Подходы с подписанными утверждениями и взаимной аутентификацией сервисов решают именно эту проблему происхождения контекста.

Постановка проблемы

Допустим, шлюз после аутентификации пользователя добавляет в запрос идентификатор и отправляет его внутреннему сервису. Если сервис доступен из пользовательской сети, через ошибочную маршрутизацию или через другой скомпрометированный компонент, злоумышленник может самостоятельно передать такой же заголовок с идентификатором привилегированного пользователя.

Последствие — подмена идентичности: проверки авторизации выполняются для одного пользователя, а фактический инициатор запроса является другим. Уязвимость может привести к чтению чужих данных, выполнению операций от имени администратора и нарушению аудита.

Даже при закрытой сети сетевое расположение не является достаточным доказательством личности вызывающего компонента. Компрометация одного внутреннего сервиса, ошибка маршрутизации или неверная настройка прокси могут превратить внутренний заголовок в управляемый атакующим параметр.

Подробное решение

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

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

Важно разделять две сущности: кто вызвал сервис и от имени какого пользователя выполняется операция. Аутентификация шлюза подтверждает только первый факт. Чтобы доверять второму, сервис должен проверить, что шлюз действительно имеет право делегировать пользовательский контекст, а утверждение относится именно к этому сервису и конкретной операции.

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

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

Ситуация из практики

В системе шлюз аутентифицировал сотрудников и передавал сервису кадровых данных два заголовка: идентификатор пользователя и его роль. Сервис был формально внутренним, но из-за правила маршрутизации его endpoint оказался доступен из пользовательской сети. Атакующий подставлял идентификатор руководителя и получал документы, доступные этой роли.

Рассматривались три варианта. Первый — разрешить доступ к сервису только по IP-адресам шлюза: это уменьшало поверхность атаки, но оставляло зависимость от сетевой конфигурации и не подтверждало пользовательскую идентичность. Второй — передавать открытые заголовки и полагаться на внутреннюю сеть: вариант был простым, но неустойчивым к компрометации или ошибке промежуточного компонента. Третий — использовать взаимную аутентификацию сервисов и короткоживущие подписанные утверждения, которые сервис проверяет самостоятельно.

Выбрали третий вариант, дополнительно закрыв прямой маршрут к сервису. Сервис стал доверять пользовательскому контексту только от аутентифицированного шлюза, проверять издателя, получателя и срок действия утверждения, а права на конкретный документ вычислять самостоятельно. В результате подмена заголовков перестала давать доступ, а журнал смог фиксировать отдельно шлюз-источник и пользователя, от имени которого выполнялась операция.

Что кандидаты часто упускают

  1. Достаточно ли взаимной аутентификации TLS, чтобы сервис доверял переданному идентификатору пользователя?

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

  1. Зачем подписанному утверждению проверять получателя, если подпись уже действительна?

Подпись подтверждает целостность и происхождение от доверенного издателя, но не гарантирует, что утверждение предназначено именно текущему сервису. Без проверки получателя корректно подписанное утверждение, выпущенное для одного сервиса, может быть принято другим сервисом с более широкими полномочиями. Проверка назначения ограничивает область применения украденного или ошибочно перенаправленного утверждения.

  1. Нужно ли сервису повторно проверять права пользователя, если роль уже передал доверенный шлюз?

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