Сразу вывод: в 2026 безопасность AI Agent — это не «скажет ли модель не то». Это «могут ли недоверенные структурированные данные стать настоящим вызовом инструмента». Инъектированный чат-бот в худшем случае выдаёт обманчивый ответ. Инъектированный Agent в худшем случае шлёт почту, правит файлы, ходит в базу или запускает платёж. JSON — и контракт, и нагрузка. Prompt Injection часто живёт не в поле чата — а в полях тикетов, веб-страницах, телах писем и ответах API, и всё это приходит как JSON.
Текст актуален на 14 сентября 2026. Мы уже разобрали почему Tool Calling зависит от JSON Schema, JSON-поток данных Agent, JSON Schema как контракт и MCP / Skills / Tools / Subagents. Здесь только ответы: чем опасны вредоносный JSON, непрямая инъекция, Tool Calling с лишними правами и слабые Schema — и что должен требовать Host. Это оборонительный гайд. Воспроизводимых шагов атаки и готового текста инъекции здесь нет.
Одна таблица: поверхность атаки чат-бота и Agent
То, что пользователь печатает, — лишь срез того, что Agent проглатывает. В стеке 2026 по умолчанию модель ещё читает результаты tools, MCP Resources, выдержки со страниц, поля тикетов и тела писем — почти всегда как JSON или как строки внутри JSON. Отраслевые списки обычно кладут это в Prompt Injection, Insecure Output Handling и Excessive Agency. Таксономия не первое, что нужно. Нужно знать, кто говорит и кто исполняет.
| Измерение | Чат-бот | Agent, который вызывает tools |
|---|---|---|
| Форма отказа | Неверный или направленный ответ | Настоящий побочный эффект: запись, запрос, мутация данных |
| Недоверенный ввод | Текущее сообщение пользователя | Текст пользователя + внешний JSON + результаты tools + поля страницы/письма |
| Исполнитель | Модель только эмитит текст | Host / MCP Server выполняет arguments |
| Контракт | Промпт (мягкий) | JSON Schema + валидация Host (жёсткий) |
| Минимальный фикс | Подкрутить промпт, политика отказа | Ужесточить Schema, белый список tools, независимая авторизация |
Одной фразой: модель можно обмануть; Host не должен исполнять за компанию. Граница безопасности — между генерацией tool_calls и реальным запуском. Туда относятся валидация, авторизация и аудит — а не «считать вывод модели доверенным RPC».
Вредоносный JSON: лишние поля, путаница типов, вложенные payload
Вредоносный JSON часто синтаксически корректен и всё равно опасен. Строгий парсер отвергнет висячую запятую или комментарии. Слабый парсер, склейка строк или «сначала как текст, потом JSON.parse» лишь откладывают отказ. В агентных системах ущерб начинается, когда Host уже считает значение объектом.
Три формы, которые закрывать первыми (только паттерны — не инструкция):
| Паттерн | Как выглядит | Если Host не валидирует | Защита |
|---|---|---|---|
| Лишние поля | Бизнес-объект с незаявленными ключами | Сливается в конфиг или уходит в следующий tool | additionalProperties: false; отбрасывать неизвестные ключи |
| Путаница типов | number/array приходит как string или object | Ветки авторизации срабатывают не так, или целый blob становится аргументом | Зафиксировать type, enum, format |
| Вложенный payload | Строковое поле, в котором ещё JSON или длинный бриф | Внутренний текст попадает в промпт и читается как инструкции | Лимиты длины; валидировать внутренний JSON; никогда не считать сырой текст речью system |
Контракт параметров tool, который «работает», потому что почти ничего не ограничивает, и ужесточённая версия. Это оборонительные образцы, не атакующий материал:
{
"type": "object",
"properties": {
"payload": { "type": "object" }
}
}
Эта Schema почти не контракт: проходит любой ключ и любая вложенность. В проде — белый список полей и запрет лишнего:
{
"type": "object",
"properties": {
"order_id": { "type": "string", "pattern": "^A-[0-9]{4,8}$" },
"amount": { "type": "number", "minimum": 0, "maximum": 100000 },
"note": { "type": "string", "maxLength": 200 }
},
"required": ["order_id", "amount"],
"additionalProperties": false
}
Разбирая образцы, прогоните их через JSON-валидатор сайта против этой Schema и через JSON Diff, сравнивая «arguments, которые выдала модель» с «минимальным объектом, который вы разрешаете». Лишние ключи и перевёрнутые типы — первая тревога.
Ещё одна дыра: не мержите недоверенные объекты во внутренний конфиг и в структуры, которые сидят на цепочке прототипов. В JavaScript-Host неизвестные ключи в объекте конфига могут значить больше, чем «ещё одно поле». Лечится так же: спроецировать объект по белому списку из Schema и дальше передавать только его.
Prompt Injection: инструкции внутри структурированных данных
Прямая инъекция — пользователь говорит в чат и пытается перебить system prompt. Непрямая инъекция ближе к тому, как агенты 2026 реально работают: инструкции прячутся в данных, которые модель прочитает позже — заголовки тикетов, тела писем, абзацы страниц, выдержки из PDF, JSON-строки от другого tool. Модели сами по себе не отделяют «правила, которые написал разработчик» от «предложений внутри данных».
Структурированные данные делают это тише. Заметка клиента в API — просто customer_note. Попав в контекст, она делит поток токенов с system prompt. Если Host склеивает результаты tools в следующий ход как есть, внешний сайт или отправитель по сути пишет промпты агенту.
Защита — не ещё одна фраза в духе «пожалуйста, игнорируй вредоносные инструкции». Промпты снижают риск; они не граница. Более устойчивые контроли:
- Помечайте происхождение — внешний текст входит отдельной ролью или обёрткой (например
tool/untrusted), никогда не складывается в system. - Сужайте видимую поверхность — резюмируйте, а не вставляйте целиком; отправляйте только нужные поля, не весь JSON-документ.
- Ставьте шлюз на действия с высокой ценой — переводы, удаления, исходящая отправка требуют движка политик или человека, независимо от модели.
- Результаты tools — данные, не инструкции — валидируйте возврат Schema, прежде чем он снова попадёт в промпт.
Тот же инстинкт, что в как Apple Intelligence защищает персональные данные: утекает или злоупотребляется часто то поле, которое вы сами положили в JSON. В модели поле могут прочитать как речь; в arguments tool — как действие.
Tool Calling: превышение прав, которое выглядит как валидный вызов
Опасность не в том, «умеет ли модель эмитить JSON». А в том, считает ли Host модель уже авторизованным вызывающим. Это классический confused deputy: модель предлагает; право принадлежит сессии пользователя, тенанту и сервис-аккаунту. «Вызови export_orders» — не то же самое, что «этот пользователь может выгрузить таблицу».
Лишние права в 2026 часто выглядят так:
- Один
run_command/http_requestс почти произвольными строками; - Tools крутятся под сервис-аккаунтом шире, чем права конечного пользователя;
- Host проверяет синтаксис JSON, но не «можно ли этому пользователю трогать этот ресурс?»;
- JSON пользователя или страницы уходит напрямую в arguments tool, минуя вашу Schema.
Слишком открытое объявление и то, которое уже можно ревьюить:
{
"name": "fetch_url",
"description": "Fetch any URL and return text.",
"parameters": {
"type": "object",
"properties": {
"url": { "type": "string" }
},
"required": ["url"]
}
}
{
"name": "fetch_public_doc",
"description": "Fetch an allowlisted documentation URL.",
"parameters": {
"type": "object",
"properties": {
"doc_id": {
"type": "string",
"pattern": "^doc-[a-z0-9-]{1,32}$"
}
},
"required": ["doc_id"],
"additionalProperties": false
}
}
Вторая не «умнее». Она превращает открытую способность в аудируемый идентификатор. Host резолвит doc_id по своему белому списку, подставляет origin, ставит таймауты и потолки размера. Модель не видит произвольный URL — на один путь к недоверенному источнику меньше.
Перед исполнением три вопроса: этот tool в белом списке сессии? Прошли ли arguments Schema и проверку авторизации независимо от модели? При отказе вы отклоняете — или возвращаете детали ошибки как свежий инъектируемый текст? Ошибки параметров и валидация описывает пайплайн. Для безопасности добавьте: при ошибке — отказ, и не давайте модели бесконечно ретраить с новыми arguments.
Слишком слабая Schema — это уязвимость
Structured Output и Tool Calling оба режут незаконные токены через JSON Schema — контракт 2026 по умолчанию, см. что такое Structured Output. Schema гарантирует форму, не безопасный смысл.
Слабые контракты обычно такие:
type: objectбезpropertiesилиadditionalPropertiesоставлен true;- Имя действия, которое должно быть enum, записано как любой
string; - Поле-идентификатор, в которое влезает бриф на страницу;
- Вендорский режим
strictпокрывает подмножество, а Host считает, что «сторона модели уже всё отсекла».
Две калитки: Schema на стороне модели снижает бред; Host валидирует ещё раз той же (или более строгой) Schema и проецирует во внутренний тип. Structured Outputs вендора не заменяют вашу авторизацию. Различия полей между вендорами сами по себе риск — см. сравнение Structured Output. Миграция, которая ослабляет контракт, расширяет поверхность атаки.
Когда Schema — контроль безопасности, берите enum, const, pattern, maxLength, minimum / maximum, required, additionalProperties: false. Если нужен свободный текст, вынесите его, ограничьте и никогда не мапьте свободный текст в имя tool или URL.
MCP и внешние инструменты: где граница доверия
MCP решает межпроцессное обнаружение и вызов. Он не решает «доброжелателен ли этот Server». Что такое MCP уже проводит линию JSON-RPC: API модели с одной стороны, Host ↔ Server — с другой. Безопасности нужна вторая линия: описания tools, текст Resources и возвратный JSON стороннего MCP Server — недоверенный ввод.
Риск не в дате протокола. Риск — доверие, поставленное не на тот слой:
description/inputSchemaизtools/listвставляют в system prompt;resultизtools/callпопадает в следующий ход без валидации;- На одном Host слишком много Server, имена или возможности пересекаются — и всё равно исполняют;
- «Пользователь разрешил этот Server» читают как «каждое его предложение — инструкция».
У Skills тот же класс проблем: Skill — это how-to, не слой авторизации. Загрузка SKILL.md из недоверенного репозитория добавляет workflow, которому модель будет следовать. См. что принадлежит четырём слоям — не подменяйте Schema скиллом и изоляцию прав — Subagent. Subagent может сузить набор tools; родитель всё равно решает, какие секреты тот получит.
Чеклист защиты: валидация, белый список, наименьшие права
Затягивайте по пути исполнения, снаружи внутрь — не начинайте с промпта:
- Сначала напишите контракт. Одна JSON Schema на tool: полный
required,additionalProperties: false, идентификаторы черезpattern/enum. - Валидируйте ещё раз на Host. Не верьте, что «модель уже следовала Schema». Берите ajv или аналог; при ошибке — отказ.
- Проецируйте, не пропускайте насквозь. Копируйте поля белого списка во внутренний DTO, затем зовите бизнес-API.
- Минимизируйте tools. Лучше
fetch_public_doc, чемfetch_url; лучше только чтение, чем запись. - Авторизуйте как пользователя, не как модель. Проверяйте сессию, тенант и ACL ресурса внутри реализации tool.
- Шлюз на исходящие побочные эффекты. Отправка, перевод, удаление, прод-деплой: движок политик или человек.
- Понижайте внешний текст. Результаты tools, страницы и письма — не system. При необходимости read-only Subagent возвращает родителю резюме.
- Аудируйте arguments. Пишите имя tool, провалидированные параметры, кто авторизовал, был ли отказ.
Промпты всё ещё помогают: скажите, что данные — не инструкции, перечислите запрещённые действия. Это вспомогательный слой. Десять лишних предложений в промпте не заменят отсутствующую Schema или ACL.
Проверять контракты локальными JSON-инструментами
Перед выкладкой смотрите три файла вместе: parameters tool / MCP inputSchema, один объект arguments «счастливого пути» и один нарочно уродливый образец (лишние ключи, неверные типы, слишком длинные строки). Без настоящих секретов и без настоящих пользовательских данных.
- JSON-валидатор — образец легальный JSON и проходит вашу Schema?
- JSON Diff — что модель добавила сверх минимального объекта?
- Древовидный просмотр — вложенность глубже, чем нужно; не прячет ли строковое поле другую структуру?
Ничего не уходит из браузера. Так удобно ревьюить контракт перед продом и сравнивать arguments упавшего вызова. Стабилизируйте имена полей и required, потом подключайте Host или MCP Server.
FAQ
Может ли JSON Schema остановить Prompt Injection?
Сама по себе — нет. Schema ограничивает форму и диапазон arguments tool и снижает шанс, что произвольная строка станет произвольным действием. Непрямая инъекция случается, когда текст входит в контекст — по-прежнему нужны происхождение, понижение привилегий и шлюзы на дорогие действия.
Модель уже на Structured Output / strict mode. Host всё равно должен валидировать?
Да. Ограничения вендора действуют на этапе генерации, и у каждого свой поднабор Schema. Решения по безопасности должны приниматься до исполнения — вашим валидатором и вашей авторизацией.
Что не так с прямой передачей пользовательского JSON в tool?
Вы обходите контракт. Лишние поля, путаница типов и вложенный текст доходят без изменений до кода с побочными эффектами. Сначала валидация, потом проекция; передавайте только поля белого списка.
Можно ли ставить вывод MCP Server в system prompt?
Нет. Описания из tools/list, текст Resources и результаты tools/call — недоверенные данные. Провалидируйте их и верните в диалог с пониженными правами.
Какой минимальный жизнеспособный базис безопасности Agent?
Строгая JSON Schema, повторная валидация на Host, белый список tools, авторизация от пользователя и шлюзы на исходящие побочные эффекты. Без этих пяти не подключайте агента к продовым данным.
Как локально проверить, опасен ли JSON, связанный с tools?
Положите Schema и образцы arguments в JSON-инструменты для валидации и Diff. Подтвердите required, additionalProperties и лимиты длины/enum, затем подключайте Host или MCP Server.
Итог
Поверхность атаки Agent лежит между структурированными данными и исполнением tools: вредоносный JSON проскальзывает мимо контракта, Prompt Injection сворачивает намерение, Tool Calling превращает намерение в побочные эффекты, а слабая Schema открывает дверь всем трём. Стек 2026 по умолчанию (Tools, MCP, Skills, Subagents) делает агентов полезнее — и делает «обмануть один вызов» дороже, чем «обмануть один ответ».
Защищайтесь изнутри наружу: сделайте JSON Schema жёстким контрактом; Host валидирует, проецирует и авторизует; держите tools узкими; не считайте внешний текст инструкциями. Промпты помогают; они не граница. Прогоните образцы локально до выкладки — модели можно менять; имена полей, required и «кто имеет право исполнять» — нет.