
Подмена субъекта в Keycloak: не кнопка, а модель безопасности
Запрос «а дайте саппорту возможность заходить под пользователем» почти всегда звучит как простая фича. На практике это изменение в модели авторизации, аудита и ответственности. В терминах Keycloak задача формулируется не как «войти под пользователем», а как «разрешить субъекту A получить токен, в котором он будет субъектом Б, с контролируемыми правами и прозрачным аудитом». Это принципиально важно, потому что Keycloak оперирует токенами, клиентами и правами на их выпуск, а не кнопками на экране.
Почему встроенная подмена субъекта — не решение
В админ-консоли Keycloak есть функция подмены субъекта, которая позволяет завершить текущую административную сессию, создать сессию пользователя и переключить контекст работы так, будто действия выполняются уже от его имени.
Для разработки и тестирования это удобно. Для реализации — проблемно. Основные узкие места:
- теряется информация об инициаторе действий;
- в логах остаётся только пользователь;
- аудит становится неполным;
- невозможно доказать, что действия выполнял сотрудник поддержки.
Это нарушение базового требования непрерывность идентичности субъекта.
Корректный подход: обмен ключами доступа
Keycloak предоставляет механизм обмена ключами доступа, который решает задачу на уровне протокола.
Как это работает
Клиент (например, администратор) с активным ключом доступа совершает следующие действия:
- отправляет запрос на `/protocol/openid-connect/token`;
- указывает `grant_type=urn:ietf:params:oauth:grant-type:token-exchange`;
- передаёт: `subject_token` (текущий ключ доступа сотрудника); `requested_subject` (ID пользователя); опционально `scope` и `audience`.
Keycloak в этот момент:
- проверяет права клиента;
- сверяет разрешения на подмену субъекта;
- выпускает новый ключ доступа с другим `sub`.
Основные настройки в Keycloak
С Keycloak всё как с хорошей техникой: снаружи может казаться, что сейчас нажмём пару кнопок — и всё заработает. Но если речь про обмен ключами доступа, внутри уже начинается территория правил, ограничений и аккуратных настроек.
Чтобы всё работало предсказуемо и без лишних прав, стоит пройтись по трём важным блокам.
1. Разрешение обмена ключами доступа для клиента
Сначала нужно подготовить сам клиент. Без этого функция `token-exchange` просто не взлетит, даже если дальше всё настроено правильно.
В настройках клиента:
- включите сервисные учётные записи (Service Accounts), если используется серверная часть;
- настройте разрешения клиента (Client Permissions);
- разрешите `token-exchange`.
2. Политики и разрешения
Дальше начинается самое важное: определить, кто вообще может запускать обмен ключами доступа и в каких случаях. Здесь лучше сразу задавать правила явно.
Через сервисы авторизации (Authorization Services):
- создайте политику (policy) — например, ролевую (role-based) с ролью поддержки;
- создайте разрешение для `token-exchange`;
- Ограничьте, кто может выполнять обмен и для каких пользователей он доступен, при необходимости — через атрибуты (attributes).
3. Ограничение области доступа
И ещё один важный момент: обмен ключами доступа не должен автоматически означать полный доступ ко всему. Чем точнее вы ограничите области доступа, тем спокойнее будет дальше.
Для этого:
- используйте клиентские области доступа (client scopes);
- ограничивайте роли;
- не передавайте лишние утверждения (claims).
Архитектура взаимодействия
Снаружи всё выглядит довольно просто: сотрудник нажимает кнопку и начинает работать от лица пользователя. Но под капотом в этот момент происходит аккуратная смена контекста, где важно не потерять ни логику, ни безопасность.
Если разложить процесс по шагам, сценарий выглядит так:
- Сотрудник авторизуется и получает ключ доступа, где subject = support user.
- На экране запускается действие «работать как пользователь».
- Серверная часть отправляет запрос на обмен ключа и получает новый, где subject = target user.
- После этого серверная часть использует новый ключ для запросов к сервисам.
Самое важное: не терять инициатора
На этом месте всё обычно выглядит обманчиво просто: ключ обменяли, доступ получили, сценарий работает. Но именно тут часто и прячется главная проблема.
По умолчанию новый ключ подменяет субъект. А если заранее не продумать, как сохранять информацию об инициаторе, очень быстро теряется контекст: кто именно запустил действие, от чьего имени оно произошло и что потом с этим делать в логах и разборе инцидентов.
Решение
Чтобы не потерять инициатора, стоит использовать:
- утверждение act (claim act);
- или пользовательские утверждения (claims) через сопостовители протокола (protocol mappers).
Пример структуры:
Или в пользовательском варианте:
Ведение журнала событий и аудит
Если здесь махнуть рукой на детали, система очень быстро становится непрозрачной. Формально всё работает, а по факту потом невозможно понять, кто именно что сделал и почему.
Минимальный набор выглядит так:
- логировать, кто инициировал действие;
- логировать, от чьего имени выполнялось действие;
- фиксировать время;
- фиксировать само действие;
- показывать режим подмены субъекта в интерфейсе;
- хранить такие события отдельно в журнале аудита, а не только в журнале приложения.
Без этого подмена субъекта — это уже не удобный инструмент, а потеря управляемости.
Режим должен быть виден сразу
Здесь лучше не надеяться на внимательность пользователя. Если человек работает от чужого имени, экран должен говорить об этом прямо.
Для этого обычно рекомендуют:
- добавить баннер вроде «Вы действуете как пользователь X»;
- предусмотреть заметную кнопку выхода из этого режима;
- визуально отличать такую сессию от обычной.
Это снижает риск ошибок и делает работу заметно прозрачнее.
Частая ошибка
Очень частый сценарий выглядит так: сначала всё настроили, обмен ключами доступа работает, команда довольна. А потом через неделю появляется вполне ожидаемый вопрос: кто именно выполнил это действие?
Проблема обычно в одном или сразу в нескольких местах:
- не передали утверждение об инициаторе (actor claim);
- не настроили аудит;
- не развели события в логах.
И вот тут начинается самое неприятное: исправлять такую историю после запуска долго, дорого и неудобно. Поэтому такие вещи лучше закладывать сразу, а не оставлять на потом.
Если в продукте уже назрели сложные сценарии доступа, роли, аудит и действия от имени пользователя, Mish поможет спроектировать это так, чтобы система оставалась безопасной, прозрачной и управляемой не только на запуске, но и в поддержке.
Альтернатива: без полной подмены субъекта
На практике подмена субъекта нужна не всегда. Во многих сценариях можно решить задачу аккуратнее и безопаснее, не подменяя личность пользователя целиком.
Подход может быть таким:
- сотрудник поддержки остаётся собой;
- серверная часть получает target_user_id;
- проверяет права;
- выполняет действия в контексте данных пользователя, но не от его имени.
Плюсы этого подхода в том, что он проще в реализации, безопаснее и прозрачнее в логах. Минусы в том, что он не подходит для проверки интерфейса и не всегда позволяет воспроизвести поведение клиента один в один.
Когда подмена субъекта действительно нужна
Использовать эту функцию стоит только тогда, когда без нее действительно не обойтись. Например:
- если нужно точно воспроизвести поведение пользователя;
- если важен полный контекст — роли, атрибуты, сессии;
- если иначе нельзя нормально диагностировать проблему.
Во всех остальных случаях разумно сначала посмотреть в сторону более безопасных альтернатив.
Итог
Подмена субъекта в Keycloak — это не мелочь и не быстрый обходной приём. Это полноценная часть модели безопасности, и относиться к ней лучше именно так.
Надёжная реализация обычно строится на обмене ключами доступа, строгих разрешениях, ограниченных областях доступа, передаче контекста инициатора, полноценном аудите и явно обозначенном пользовательском сценарии. Если выпадает хотя бы один из этих элементов, система становится либо уязвимой, либо непрозрачной, либо и тем и другим сразу.
Если вы не хотите собирать такие механики по частям и потом дорого исправлять архитектурные просчёты, приходите в Mish — поможем продумать логику доступа, поведение интерфейсов и техническую реализацию как единую систему.
Ещё статьи
Предыдущая статья
Как мы решили проблему хаотичной структуры проекта с Pug и Vite
Привет, я Андрей Беннер, фронтенд-разработчик в Mish. Сейчас я расскажу вам о нашем опыте в оптимизации хаотичных процессов с помощью собственных разработок. Если вы когда-либо работали с проектами на стеке Vite + Pug + SCSS + TypeScript, то наверняка сталкивались с хаосом в файловой структуре и рутиной, мешающей сосредоточиться на главном — решении задач бизнеса и создании удобного интерфейса для пользователя. В этом материале мы поделимся тем, как столкнувшись с проблемой, создали решение, которое сделало процесс разработки более эффективным, а структуру проекта — интуитивно понятной. А ещё расскажем, как это решение родилось внутри команды и стало частью нашего вклада в open source.

Следующая статья
Маленькое решение для больших целей
А помните, был бум популярности на ботов, и все компании быстро выпускали своих фирменных помощников в Telegram, VK и на других площадках? Вот только боты стали уже базовым винтиком в системах коммуникации бизнеса. Пользователь же стал требователен и помимо работающего решения начал стремиться к красоте. И тут на помощь приходят мини-приложения. Рассказываем, что это и чем они могут быть полезны бизнесу.

