Назад в блог

AI-агент в корпоративном мессенджере: чьи права он наследует

Агент в Slack, Teams или Mattermost читает то, что доступно его токену, а отвечает в канал, где права у всех разные. Разбираем identity бота, пересечение прав и аудит.

Иллюстрация к материалу: AI-агент в корпоративном мессенджере: чьи права он наследует

Корпоративный мессенджер — самый удобный вход для агента. Он уже открыт у всех, в нём есть история обсуждений, файлы, ссылки на задачи, и человек привык задавать вопросы в чате. Не нужно ставить клиент, объяснять интерфейс, придумывать онбординг: упомянул бота — получил ответ.

И это же самая опасная поверхность из всех, к которым мы подключаем агентов. Потому что мессенджер — единственное место, где данные с разными правами доступа встречаются в одном окне. Агент читает канал, доступный его токену, и отвечает в канал, где сидят люди с разными правами. Проверка на входе есть, проверки на выходе обычно нет.

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

Главное за минуту

  • Проблема не в чтении, а в получателе. Агент с правами CFO законно читает зарплатные данные и отвечает в канал, где сидит джуниор.
  • Общий сервисный аккаунт — главный источник утечек. Права бота живут отдельно от прав спрашивающего и расходятся с ними.
  • Правильная модель — права инициатора на каждый запрос, а для групповых каналов — пересечение прав всех участников.
  • Текст в мессенджере — недоверенный ввод. Публичный канал доступен всем, и инъекция в нём может дотянуться до приватных данных.
  • «Да» в чате — не подтверждение. Подтверждение должно быть привязано к конкретному вызову и аргументам.
  • Аудит нужен на стороне компании, а не только на стороне вендора.

Почему мессенджер ломает привычную модель доступа

В обычном веб-приложении ответ видит только тот, кто задал вопрос. В мессенджере ответ видит канал. Это меняет всё.

Смешанная аудитория. В канале #finance могут быть финансисты, аналитики и продуктовая команда. Права на документы у них разные, а сообщение одно.

История и поиск. Бот может искать по всей истории, к которой у него есть доступ, а не только по текущему обсуждению. Один вопрос «что мы решали по бюджету» вытягивает контекст, о котором спрашивающий не знал.

Постоянство. Сообщение остаётся в канале. Даже если бот ошибся, «отозвать» ответ у всех, кто его прочитал или переслал, нельзя.

Внешние участники. Гостевые каналы, shared channels с партнёрами, боты других команд — всё это расширяет аудиторию ответа за пределы компании.

Ссылки. Мессенджеры генерируют превью ссылок: отправляешь URL — сервер мессенджера его открывает. Это отдельный канал утечки, о котором ниже.

Как это выглядит на практике

Классический сценарий описан в разборе Okta про «authorization gap»: финансовый директор ставит агента в канал, агент аутентифицируется его учётными данными, аналитик спрашивает про бюджет квартала, агент находит планы компенсаций и отвечает в канал. Формально всё правильно: CFO имел доступ к этим файлам. Фактически — зарплата руководителей утекла к джуниору.

Формулировка «authorization checked at retrieval, not at output» точно описывает механизм. Проверка прав стоит на входе в хранилище, но никто не проверяет, имеет ли право получатель видеть результат.

Тот же паттерн встречался в громких инцидентах 2025 года: агент с широкими правами читал данные корректно, а утечка происходила на выходе — через ответ, ссылку или превью. В случае с MCP-сервером для Slack, найденным Йоханом Ребергером, эксплойт использовал механизм unfurl: агент с правами администратора вставлял чувствительные данные в URL, а сервер мессенджера сам открывал ссылку и отправлял данные наружу. Клик не требовался.

Отдельный класс — инъекция через канал. В 2024 году исследователи PromptArmor показали атаку на Slack AI: вредоносная инструкция размещалась в публичном канале, который создатель атаки мог сделать в одиночку, а ассистент, отвечая другому пользователю, подтягивал данные из приватных каналов и вставлял их в ссылку. Техника попала в MITRE ATLAS как AML.CS0035. Вендор отреагировал, но схема остаётся учебной: публичный канал — это вход для инструкций, а приватные данные — цель.

Отсюда важный вывод: проблема не в том, что агент «слишком много знает». Проблема в том, что у него нет модели аудитории.

Три модели идентичности бота

Выбор модели определяет, что вообще можно проверить.

1. Общий сервисный аккаунт. Бот имеет собственные права: обычно широкие, потому что так проще. Все пользователи работают через один токен.

Плюсы: просто настроить, работает одинаково для всех. Минусы: права бота не связаны с правами спрашивающего, и в аудите видно «бот прочитал», а не «бот прочитал по запросу Анны». Разрыв между тем, что разрешено боту, и тем, что должен видеть человек, — источник утечек.

2. Токен пользователя. Каждый запрос выполняется от имени того, кто его инициировал. Так работает, например, «multiplayer tool calling» в Mattermost: человек вызывает агента в канале, и агент действует в рамках его прав.

Плюсы: чтение соответствует правам человека. Минусы: остаётся вопрос получателя — если Анна спросила в общем канале, её права всё равно шире, чем у аудитории канала.

3. Токен инициатора + пересечение прав аудитории. Самый строгий вариант: агент читает в рамках прав инициатора, но перед выдачей ответа вычисляет, что из найденного доступно всем участникам канала, и отдаёт только это.

Именно третья модель закрывает «authorization gap». Она дороже: нужно знать состав аудитории и уметь считать пересечение прав в реальном времени. Но только она отвечает на вопрос «кому именно мы это сейчас показали».

Пересечение прав: как считать

Идея простая: результат — это множество фактов, доступных каждому участнику. Формально — пересечение множеств доступных ресурсов.

Практическая реализация:

  1. Определите аудиторию. Для DM — два человека. Для приватного канала — список участников. Для публичного — всех участников workspace (и это уже почти всегда «не отдавать ничего чувствительного»).
  2. Соберите права. Источник прав — не список участников канала, а система, которая знает доступ к документам: файловое хранилище, CRM, трекер, база знаний.
  3. Посчитайте пересечение до выборки. Важно: не «прочитать всё и отфильтровать», а сузить запрос заранее. Иначе данные уже попали в контекст модели и к провайдеру.
  4. Примените как ограничение в запросе. Если система позволяет — фильтр по ACL на стороне источника. Если нет — предварительный отбор идентификаторов ресурсов, доступных всем.
  5. Отметьте срез в ответе. «Показано то, что доступно всем участникам канала» — честная формулировка, которая снижает ожидания и объясняет пропуски.

Для DM пересечение — это права одного человека, поэтому там достаточно токена инициатора. Основная сложность начинается в каналах.

Отдельно про публичные каналы: технически пересечение прав всех участников workspace часто пустое для чувствительных данных. Это нормальный ответ: в публичный канал идёт только то, что не жалко показать всем.

Политика по типу канала

Одна политика на все каналы не работает. Разумная градация:

Тип каналаЧтениеЗаписьОсобенности
DM с ботомправа инициатораразрешено с подтверждениемсамый безопасный сценарий, с него начинают
Приватный каналпересечение прав участниковподтверждение + проверка аудиториинужен список участников и ACL
Публичный каналтолько публичные источникиобычно запрещеноинъекции приходят именно отсюда
Гостевой/sharedтолько явно разрешённые источникизапрещеновнешняя аудитория, утечка необратима

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

Что делать с инъекциями

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

Меры, которые работают:

  • Разделяйте роли текста. Сообщения пользователей — данные, а не инструкции. Системная политика неизменяема и не может быть расширена текстом из канала.
  • Не давайте агенту инструментов записи в том же цикле, где он читает внешний текст. Сначала чтение и ответ, потом отдельный шаг с подтверждением.
  • Отключайте автоматическое раскрытие ссылок для ответов агента или ограничивайте домены. Unfurl — это исходящий запрос от имени вашей инфраструктуры.
  • Проверяйте вложения. Документ, приложенный в канал, — такой же недоверенный ввод, как и текст.
  • Ограничивайте, что агент вообще читает. Бот, который отвечает на вопросы по одному каналу, не должен уметь искать по всей истории workspace.

Память агента: где живёт контекст между сообщениями

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

Три типичные ошибки.

Общая память на всех. Агент сохраняет факты из DM в общее хранилище и потом использует их в публичном канале. Формально это удобно, фактически — обход прав аудитории, потому что данные попали в другой контекст.

Память без срока. Факт, сохранённый полгода назад, уже может быть неверным (сменился ответственный, закрылся проект), а агент продолжает на него опираться. Нужен TTL и явная процедура забывания.

Память без источника. Запись «клиент X на грани ухода» без указания, откуда она взялась, невозможно проверить. Храните ссылку на сообщение или документ, а не только текст.

Практические правила: память привязана к паре «пользователь + канал» и не перетекает между ними автоматически; запись в память — отдельная операция с валидацией; при выходе человека из канала его данные не становятся доступны остальным через память бота; на запрос «что ты обо мне знаешь» агент обязан показать сохранённое.

Формально это выглядит как отдельная политика на память:

memory:
  scope: [user, channel]
  cross_channel_read: deny
  ttl_days: 90
  require_source: true
  on_member_leave: purge_channel_scope
  on_request: show_stored_facts

Здесь важны две строки. cross_channel_read: deny не даёт использовать факт из DM в публичном ответе. on_member_leave: purge_channel_scope закрывает сценарий, когда человек уходит из канала, а бот продолжает пересказывать его реплики оставшимся.

Подтверждения в чате

«Подтвердить?» с кнопкой в чате — удобно и опасно. Опасность в том, что подтверждение легко оторвать от действия.

Типичные ошибки:

  • кнопка подтверждает «последнее действие» агента, а не конкретный вызов с аргументами;
  • подтверждение действует какое-то время и покрывает следующие похожие вызовы;
  • нажать может любой участник канала, включая того, у кого нет прав на операцию;
  • ответ «да» текстом засчитывается как согласие.

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

Если операция необратима, одного подтверждения мало: нужна карточка с суммой, получателем, последствиями и явной формулировкой «это нельзя отменить».

Аудит: что должно остаться у компании

Вопрос из практики governance: если разбор инцидента требует логов вендора, вы не управляете процессом. Нужна своя запись.

Минимальный набор на каждый запрос:

{
  "request_id": "req_01J9...",
  "channel": { "id": "C0241", "type": "private", "members": 7 },
  "initiator": "user:anna@example.com",
  "agent": "agent:ops-helper",
  "identity_model": "initiator_scoped",
  "audience_intersection": "computed",
  "tools_called": ["search_wiki", "get_incident"],
  "sources_included": ["wiki:oncall", "tracker:INC-4412"],
  "sources_excluded": 3,
  "approval_id": null,
  "result": "posted"
}

Поле sources_excluded часто важнее остальных: оно показывает, что агент нашёл данные, но не отдал их из-за прав аудитории. Без него невозможно отличить «ничего не нашёл» от «нашёл и скрыл».

Для разбора инцидента нужны ещё две вещи: кто был в канале на момент ответа (состав меняется) и какие права были у каждого. Хранить полный ACL в аудите тяжело, но снапшот ссылки на версию прав — реально.

Рабочий сценарий: сводка по каналу

Разберём типовую просьбу: «сделай сводку по #incident-payments за неделю».

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

Хорошая реализация:

  1. Агент получает запрос с идентичностью инициатора и каналом.
  2. Проверяется тип канала и состав участников на текущий момент.
  3. Чтение идёт в рамках прав инициатора: сообщения канала, вложения, ссылки на тикеты.
  4. Перед формированием ответа вычисляется пересечение: какие из найденных источников доступны всем участникам. Документы, доступные не всем, помечаются как исключённые.
  5. Модель получает только разрешённый срез — то есть приватные документы не попадают в контекст вообще.
  6. Ответ публикуется с пометкой о срезе и списком использованных источников.
  7. В аудит пишутся инициатор, канал, инструменты, включённые и исключённые источники, число участников.

Что это даёт: сводка остаётся полезной (обсуждение в канале доступно всем участникам), а приватные вложения не утекают. Если человеку нужна деталь из закрытого документа, он спрашивает в DM — там пересечение прав равно его правам.

Конфигурация политики

Формализуем правила в конфиге, который читает шлюз перед вызовом:

channel_policy:
  dm:
    identity: initiator
    read: initiator_scope
    write: { require_approval: true }
  private:
    identity: initiator
    read: audience_intersection
    write: { require_approval: true, require_member_rights: true }
  public:
    identity: initiator
    read: public_sources_only
    write: { deny: true }
  external_shared:
    identity: initiator
    read: allowlist
    write: { deny: true }

output:
  unfurl: deny
  link_domains: [wiki.internal, tracker.internal]
  redact: [pii, credentials, bank_accounts]

approval:
  bind_to: args_hash
  ttl_seconds: 600
  single_use: true
  eligible: members_with_rights

Здесь важны три решения. read: audience_intersection для приватных каналов — то, что закрывает основную дыру. unfurl: deny убирает zero-click канал утечки. eligible: members_with_rights не даёт подтвердить операцию человеку, у которого нет прав её выполнить.

Порядок внедрения

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

  1. DM с ботом, только чтение. Права инициатора, никакой записи. Здесь проверяется качество ответов и поведение модели.
  2. DM, запись с подтверждением. Один тип операции: создать задачу, поставить напоминание. Проверяется механика подтверждений.
  3. Приватные каналы, чтение с пересечением прав. Появляется аудитория, проверяется расчёт пересечения и аудит.
  4. Публичные каналы, только публичные источники. Проверяется устойчивость к инъекциям.
  5. Внешние каналы — по исключению. Обычно лучше запретить совсем.

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

Чеклист

  • Определена модель идентичности: общий аккаунт, токен инициатора или токен + пересечение.
  • Для каналов считается пересечение прав аудитории, а не только права инициатора.
  • Публичные и внешние каналы читают только разрешённые источники.
  • Автоматическое раскрытие ссылок отключено или ограничено доменами.
  • Сообщения и вложения считаются недоверенным вводом.
  • Подтверждения привязаны к хешу аргументов, одноразовые, с коротким TTL.
  • Подтвердить операцию может только тот, чьи права её покрывают.
  • Есть собственный аудит: инициатор, аудитория, источники, исключения, решение.
  • Есть план на инцидент: как отозвать доступ и удалить ответ.
  • Модель инференса соответствует требованиям по данным.

FAQ

Можно ли просто выдать боту права администратора и не усложнять? Можно, но тогда каждый вопрос в канале — это потенциальная утечка, а аудит показывает только действия бота. Права администратора нужны для настройки, а не для чтения.

Что если в канале 200 человек? Пересечение прав быстро становится пустым для чувствительных данных. Это ожидаемо: для больших каналов агент отвечает общими фактами, а детали — в DM.

Как быть с гостями? Считать их участниками аудитории при расчёте пересечения. Если это технически невозможно — запретить чтение внутренних источников в таких каналах.

Slack AI уже учитывает права — зачем что-то ещё? Встроенные ассистенты действительно проверяют права при поиске. Но как только вы подключаете свой агент с инструментами и внешними системами, проверка прав на выходе становится вашей задачей.

Нужен ли отдельный бот для каждого отдела? Не обязательно, но полезно: разные наборы инструментов и разные политики проще, чем один универсальный агент с максимальными правами.

Где Codenik

Codenik закрывает ту часть, которую мессенджер не решает: он стоит между агентом и корпоративными системами и знает, от чьего имени идёт запрос. Для каждого вызова проверяются identity инициатора, права на инструмент и ограничения по аудитории, а секреты подставляются на стороне шлюза, поэтому в канал и в контекст модели они не попадают.

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

Источники

  • Slack, Security for AI features in Slack: доступ только к данным, которые видны участнику, отсутствие обучения на данных клиента.
  • PromptArmor, «Data Exfiltration from Slack AI via Indirect Prompt Injection» (2024); техника описана в MITRE ATLAS как AML.CS0035.
  • Okta, «AI Agent Security: The Authorization Gap»: проверка прав при извлечении, но не при выдаче; сценарий с агентом в канале.
  • Mattermost, материалы о Sovereign AI и Agents: multiplayer tool calling под правами пользователя, Tool Policy Editor, аудит использования токенов.
  • Mattermost, «Agents Are Doing the Work. Now Control What They Can See»: риск общего сервисного аккаунта и утечка через сводку канала.
  • Разборы уязвимостей MCP-серверов для мессенджеров 2025 года, включая zero-click утечку через unfurl.