Безопасность ИИ-агентов: вредоносный JSON, prompt injection, Tool Calling и риски структурированных данных

На 14 сентября 2026: если агент вызывает инструменты и читает внешний JSON, поверхность атаки смещается с обманчивого ответа на реальный вызов. Оборонительная карта: вредоносный JSON, непрямая инъекция, слишком широкие инструменты и слабые схемы.

Сразу вывод: в 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 не валидируетЗащита
Лишние поляБизнес-объект с незаявленными ключамиСливается в конфиг или уходит в следующий tooladditionalProperties: 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; родитель всё равно решает, какие секреты тот получит.

Чеклист защиты: валидация, белый список, наименьшие права

Затягивайте по пути исполнения, снаружи внутрь — не начинайте с промпта:

  1. Сначала напишите контракт. Одна JSON Schema на tool: полный required, additionalProperties: false, идентификаторы через pattern / enum.
  2. Валидируйте ещё раз на Host. Не верьте, что «модель уже следовала Schema». Берите ajv или аналог; при ошибке — отказ.
  3. Проецируйте, не пропускайте насквозь. Копируйте поля белого списка во внутренний DTO, затем зовите бизнес-API.
  4. Минимизируйте tools. Лучше fetch_public_doc, чем fetch_url; лучше только чтение, чем запись.
  5. Авторизуйте как пользователя, не как модель. Проверяйте сессию, тенант и ACL ресурса внутри реализации tool.
  6. Шлюз на исходящие побочные эффекты. Отправка, перевод, удаление, прод-деплой: движок политик или человек.
  7. Понижайте внешний текст. Результаты tools, страницы и письма — не system. При необходимости read-only Subagent возвращает родителю резюме.
  8. Аудируйте 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 и «кто имеет право исполнять» — нет.