💉 Безопасность ИИ-агентов: боремся с косвенными промпт-инъекциями
Самое время поиграть с рисками косвенных промпт-инъекций в агенте, описанном в предыдущем посте.
⚠ TL;DR: коммит с соответствующими изменениями можно посмотреть здесь, ниже — его подробный разбор + необходимая база.
Здесь нужно прям на старте понимать, что проблема IPI обусловлена современной архитектурой LLM, для которой промпт — есть промпт, и неважно, какая часть этого промпта является данными, а какая — инструкциями по их обработке. Победить эту проблему было бы возможно переходом, по аналогии с вычислительными системами — от «принстонского» подхода к «гарвардскому», в котором потоки данных и инструкций были бы физически разделены. Думаю, нет смысла объяснять, почему в обозримом будущем этого ждать не стоит. А значит — время костылей 🤩 Костыли на эту тему можно условно разделить на три группы:
1️⃣ Навесные защиты, aka «файрвол для LLM». Стоят в разрыве между агентом и модель, перехватывая все запросы и ответы на них, и детектирующие в т.ч. условия IPI, как правило, с помощью той же LLM, со специально наполняемым по ходу агентского цикла контекстом, и детекторами аномалий.
2️⃣ Разделение harness (оркестрации) и compute (вычислений). Фундаментальный подход, предполагающий вынос всей работы с недоверенными данными в субагентов, огороженных песочницей. Этот подход, предлагается, например, в OpenAI Agents SDK. Работает неплохо, если конечно у вас есть субагенты и песочница 😬
3️⃣ Контрмеры внутри самого агента. Опираясь на функциональность агента, его бизнес-логику и модель угроз, позволяют довольно-таки существенно снизить риски, связанные с IPI.
О каком пункте пойдет речь далее, думаю, отдельно уточнять не нужно)
🧍♂ Модель угроз
У deeper-bot нет инструментов, позволяющих читать или изменять своё окружение, а системный промпт и так открыт, поэтому единственным защищаемым активом являются данные пользователя (сообщения и загруженные документы, т.е. содержимое контекстного окна). Таким образом, речь идет о контрмерах против эксфильтрации пользовательских данных через косвенные промпт-инъекции.
У агента ровно три источника недоверенных данных:
• результаты поиска (тула web_search);
• загруженный веб-контент (тула web_fetch);
• загруженные пользователем документы (т.к. не факт, что они созданы именно им).
Эти данные by-design попадают в контекстное окно в исходном виде, но могут выстрелить ещё в трех случаях:
• суммаризация веб-контента (происходит, когда оказывается загружено слишком много данных из одного источника);
• суммаризация отчета (поскольку сам отчет может включать в себя цитаты из внешних источников);
• сообщения об ошибках тулов, уходящие в LLM (могут содержать текст ответа сервера).
Непосредственно эксфильтрация данных возможна только с помощью тулы web_fetch, через параметры запроса.
Соответственно, план по контрмерам вырисовывается следующий:
1️⃣ Все недоверенные данные, попадающие в контекстное окно явно или косвенно, размечаем соответствующими untrusted-тегами. Не позволяем этим тегам приходить из внешнего контента, через их экранирование.
2️⃣ Инструктируем модель о назначении untrusted-тегов и явно запрещаем следовать инструкциям из размеченных ими блоков.
3️⃣ Поскольку, модель зачастую вертеть хотела то, о чем ты там её инструктировал, на всякий случай разрешаем агенту запрашивать контент только с тех ресурсов, которые указал пользователь, и с полученных из поисковой выдачи.
4️⃣ Поскольку модель также иногда хочет вертеть и наличие web_search, осуществляя поиск прямыми запросами через web_fetch (что, впрочем, существенно улучшает результаты исследований), вводим также белый список разрешенных доменов поисковиков, с которых агент может запрашивать контент.
Продолжение — в следующем посте.
Самое время поиграть с рисками косвенных промпт-инъекций в агенте, описанном в предыдущем посте.
⚠ TL;DR: коммит с соответствующими изменениями можно посмотреть здесь, ниже — его подробный разбор + необходимая база.
Косвенная промпт-инъекция (Indirect prompt injection, IPI) — это метод атаки на LLM, при котором злоумышленник внедряет вредоносные инструкции во внешние данные (сайты, документы), заставляя ИИ-агента выполнить их вместо исходной задачи пользователя.
Здесь нужно прям на старте понимать, что проблема IPI обусловлена современной архитектурой LLM, для которой промпт — есть промпт, и неважно, какая часть этого промпта является данными, а какая — инструкциями по их обработке. Победить эту проблему было бы возможно переходом, по аналогии с вычислительными системами — от «принстонского» подхода к «гарвардскому», в котором потоки данных и инструкций были бы физически разделены. Думаю, нет смысла объяснять, почему в обозримом будущем этого ждать не стоит. А значит — время костылей 🤩 Костыли на эту тему можно условно разделить на три группы:
1️⃣ Навесные защиты, aka «файрвол для LLM». Стоят в разрыве между агентом и модель, перехватывая все запросы и ответы на них, и детектирующие в т.ч. условия IPI, как правило, с помощью той же LLM, со специально наполняемым по ходу агентского цикла контекстом, и детекторами аномалий.
2️⃣ Разделение harness (оркестрации) и compute (вычислений). Фундаментальный подход, предполагающий вынос всей работы с недоверенными данными в субагентов, огороженных песочницей. Этот подход, предлагается, например, в OpenAI Agents SDK. Работает неплохо, если конечно у вас есть субагенты и песочница 😬
3️⃣ Контрмеры внутри самого агента. Опираясь на функциональность агента, его бизнес-логику и модель угроз, позволяют довольно-таки существенно снизить риски, связанные с IPI.
О каком пункте пойдет речь далее, думаю, отдельно уточнять не нужно)
🧍♂ Модель угроз
У deeper-bot нет инструментов, позволяющих читать или изменять своё окружение, а системный промпт и так открыт, поэтому единственным защищаемым активом являются данные пользователя (сообщения и загруженные документы, т.е. содержимое контекстного окна). Таким образом, речь идет о контрмерах против эксфильтрации пользовательских данных через косвенные промпт-инъекции.
У агента ровно три источника недоверенных данных:
• результаты поиска (тула web_search);
• загруженный веб-контент (тула web_fetch);
• загруженные пользователем документы (т.к. не факт, что они созданы именно им).
Эти данные by-design попадают в контекстное окно в исходном виде, но могут выстрелить ещё в трех случаях:
• суммаризация веб-контента (происходит, когда оказывается загружено слишком много данных из одного источника);
• суммаризация отчета (поскольку сам отчет может включать в себя цитаты из внешних источников);
• сообщения об ошибках тулов, уходящие в LLM (могут содержать текст ответа сервера).
Непосредственно эксфильтрация данных возможна только с помощью тулы web_fetch, через параметры запроса.
Соответственно, план по контрмерам вырисовывается следующий:
1️⃣ Все недоверенные данные, попадающие в контекстное окно явно или косвенно, размечаем соответствующими untrusted-тегами. Не позволяем этим тегам приходить из внешнего контента, через их экранирование.
2️⃣ Инструктируем модель о назначении untrusted-тегов и явно запрещаем следовать инструкциям из размеченных ими блоков.
3️⃣ Поскольку, модель зачастую вертеть хотела то, о чем ты там её инструктировал, на всякий случай разрешаем агенту запрашивать контент только с тех ресурсов, которые указал пользователь, и с полученных из поисковой выдачи.
4️⃣ Поскольку модель также иногда хочет вертеть и наличие web_search, осуществляя поиск прямыми запросами через web_fetch (что, впрочем, существенно улучшает результаты исследований), вводим также белый список разрешенных доменов поисковиков, с которых агент может запрашивать контент.
Продолжение — в следующем посте.