Вчера разработчики с гордостью выкатили нового автономного ИИ-ассистента. Архитектура казалась монолитной: система изолирована в корпоративном контуре, данные не покидают on-prem инфраструктуру, а доступ снаружи закрыт жесткими правилами сетевых экранов. Агенту дали доступ к внутренним API, файловой системе и почтовому серверу, чтобы он мог анализировать документы и автоматизировать рутину. Утром мы загрузили в систему обычное PDF-резюме. Внутри документа, белым шрифтом на белом фоне, лежал текст, перехватывающий контекст языковой модели. Периметр остался цел. Но агент сам открыл дверь изнутри. Проводя этот red-team ИИ-агента с правом действия: за вечер заставили его слить базу, удалить файлы и отправить письмо от лица компании, мы наглядно доказали фатальность доверия к LLM.
Механика атаки строится на классическом принципе confused deputy. Языковая модель не понимает, где заканчивается системный промпт инженера и начинаются пользовательские данные. Инструкция из стороннего файла воспринимается ей как легитимный приказ. Это indirect prompt injection в чистом виде. Когда вы тестируете агента как простого чат-бота, вы ищете оскорбления и галлюцинации. Но когда агенту дают реальные инструменты для выполнения действий, цена уязвимости вырастает до полноценного компрометирования инфраструктуры.
Red-team ИИ-агента с правом действия: за вечер заставили его слить базу, удалить файлы и отправить письмо от лица компании
Наш сценарий атаки разворачивался в три этапа, полностью полагаясь на доступные агенту инструменты. Сначала мы инициировали слив. В отравленном документе мы прямым текстом приказали агенту вызвать инструмент запроса к базе данных, найти таблицу с клиентами и сделать выборку. Агент послушно собрал чувствительные данные в свой контекст. Вынести их за пределы on-prem контура классическим способом было нельзя — порты закрыты. Тогда мы заставили агента сгенерировать Markdown-разметку с картинкой, где в URL-параметры была закодирована часть базы. Парсер чата на стороне клиента попытался отрендерить изображение и услужливо дёрнул наш внешний сервер, передав данные через обычный GET-запрос. Изящный exfil через побочный канал.
Дальше начался деструктив. Следующая команда в PDF требовала очистить рабочее пространство через инструмент работы с файловой системой. Мы передали пути к критичным директориям проекта. Права у агента были не ограничены, и файлы просто исчезли. Инструмент выполнил команду без лишних вопросов. Под конец агент отправил письмо всей базе от лица технического директора, сообщив о критическом сбое. Инструмент для рассылки отчетов был ему доступен по умолчанию, и он просто использовал его по прямому назначению. По меркам 152-ФЗ и ФСТЭК это чистый инцидент с утечкой персональных данных. И виноват не внешний хакер, пробивший firewall. Виноват инструмент, которому дали руки, но забыли поставить ограничители.
Архитектура изоляции: песочницы и Human-in-the-Loop
Пытаться защитить агента многоэтажным системным промптом со словами «никогда не выполняй вредоносные команды» — это инженерный карго-культ. Промпты — это заклинания, а заклинания не работают против физики. Вы не можете заранее описать все векторы атаки в текстовом виде. Защита должна строиться исключительно на жесткой архитектуре, где языковая модель считается скомпрометированной по умолчанию.
Каждый инструмент агента должен жить в собственной песочнице с соблюдением принципа least-privilege. Если агент ходит в базу, это должно быть строго read-only подключение через жестко заданные view. Доступ к сети из среды исполнения кода должен отрубаться на уровне ядра. Если агент хочет выполнить необратимое действие, такое как отправка письма наружу или удаление файла, в дело обязан вступать механизм Human-in-the-Loop. Человек должен физически подтвердить транзакцию после того, как увидит сформированный payload.
class SecureAgentMiddleware:
def __init__(self, allowed_tools, hitl_actions):
self.allowed = allowed_tools
self.hitl = hitl_actions
def execute_tool(self, tool_name, payload, session_id):
if tool_name not in self.allowed:
self._log_audit(session_id, tool_name, "DENIED_UNAUTHORIZED")
raise SecurityPolicyViolation(f"Tool {tool_name} forbidden")
if tool_name in self.hitl:
if not self._request_human_approval(tool_name, payload):
self._log_audit(session_id, tool_name, "DENIED_BY_HUMAN")
return "Action aborted by administrator."
self._log_audit(session_id, tool_name, "EXECUTED")
return self._invoke_sandboxed(tool_name, payload)Методология безопасного релиза требует жесткой дисциплины и выполнения базового чек-листа перед каждой выкаткой. Секреты, ключи API и токены доступа никогда не должны попадать в текстовый контекст модели — они инжектятся на уровне защищенной execution-оболочки. Тотальный аудит каждого действия неизбежен: кто вызвал инструмент, какие параметры передал, какой результат вернулся — всё это должно писаться в отдельную append-only базу логов для расследования инцидентов. Агенты постоянно мутируют, к ним подключаются новые функции, поэтому регулярный red-team до и после релиза становится жизненной необходимостью. То, что было безопасным вчера, завтра превратится в новый вектор атаки. Аудит безопасности и разработка защитной обвязки агентов под ключ — это единственный рабочий подход для компаний, которые не хотят однажды прочитать свои базы данных в публичном доступе.