Блог • Agent Security
Почему AI-агенту нельзя доверять только JSON Schema: проверка, недоверенный ввод и решение о правах
Вызов инструмента, прошедший JSON Schema, показывает только верную форму аргументов. Письмо, изменение файла или выход наружу требуют второго JSON: решения о правах. Если decision не allow, вызов останавливается.
Зачем выводу нужна форма — в статье зачем ИИ нужен JSON Schema. Кто отвечает за безопасность при многих серверах инструментов — в статье безопасность и права MCP. Здесь только решение после верной формы: список разрешённых, grant и allow / deny / ask.
- Схема отвечает только на вопрос о форме
- Имя инструмента уже должно быть в вашем списке разрешённых
- Решение о правах — отдельный JSON
- Если в grant нет этой способности, инструмент не выполняйте
Сначала это: Вызов верной формы всё равно может быть вызовом, который нельзя выполнять. Дальше: что доказывает схема, как выглядит решение, прохождение, которое вы всё равно отклоняете, и где останавливаются три случая.
Что доказывает схема
JSON Schema проверяет типы, enum и обязательные ключи. См. JSON Schema. Она не доказывает, что этой сессии можно отправлять почту, и не доказывает, что вы проверяли это имя инструмента.
{
"tool": "send_email",
"arguments": {
"to": "ops@example.com",
"subject": "status",
"body": "ok"
}
}
Когда to, subject и body — строки, схема аргументов видит только типы. Она не видит, что grant этой сессии содержит read_inbox и не содержит send.
Решение — другой JSON
Спецификация инструментов MCP требует проверки ввода и возможности человеку отказать в вызове. См. MCP Tools. После того как JSON аргументов прошёл, запишите решение. toolAllowed значит, что это имя инструмента есть в вашем списке. grant — способность, которая у сессии уже есть. decision может быть только allow, deny или ask.
{
"tool": "send_email",
"argumentsValid": true,
"toolAllowed": true,
"grant": ["read_inbox"],
"decision": "deny",
"reason": "grant_missing"
}
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["tool", "argumentsValid", "toolAllowed", "decision", "reason"],
"properties": {
"tool": { "enum": ["send_email"] },
"argumentsValid": { "type": "boolean" },
"toolAllowed": { "type": "boolean" },
"grant": {
"type": "array",
"items": { "enum": ["read_inbox", "send"] }
},
"decision": { "enum": ["allow", "deny", "ask"] },
"reason": {
"enum": ["ok", "grant_missing", "tool_not_allowlisted", "arguments_invalid"]
}
}
}
Эта схема останавливает decision вне трёх значений, argumentsValid не булева типа, отсутствие decision и значение grant вне списка. Она не может сказать, что allow требует одновременно toolAllowed и нужный grant. Эти правила остаются в приложении.
Прохождение, которое всё равно отклоняют
Измените решение выше: decision равен allow, reason равен ok, grant остаётся read_inbox. Эта схема проходит. Прохождение — не разрешение отправить письмо. Правило приложения: decision может быть allow только если argumentsValid и toolAllowed оба true и grant содержит send. Если чего-то нет, верните deny или ask и не вызывайте инструмент.
| Правило в приложении | Видит ли это схема? | Если не выполнено |
|---|---|---|
| при allow argumentsValid должен быть true | Эту связку она не видит | Поставить deny, reason равен arguments_invalid |
| при allow toolAllowed должен быть true | Эту связку она не видит | Поставить deny, reason равен tool_not_allowlisted |
| при allow в grant должен быть send | Эту связку она не видит | Поставить deny или ask, reason равен grant_missing |
Где останавливаются три случая
Пометки в описании инструмента — это подсказки. Спецификация говорит, что пометки из недоверенного источника не являются уже применённым контролем безопасности. См. пометки инструмента — не контракт. Вызов на самом деле останавливают ваша схема и это решение.
| Случай | Какой может быть форма | Где остановить |
|---|---|---|
| Форма аргументов неверна | Схема не проходит | Не писать allow и не вызывать |
| Инструмента нет в списке разрешённых | Аргументы всё ещё могут быть верными | toolAllowed равен false, decision равен deny |
| У сессии нет этой способности | Аргументы верны, инструмент в списке | В grant нет способности, значит deny или ask |
Недоверенный текст может привести к тому, что модель предложит вызов, а недоверенное описание инструмента может показать опасную способность как обычный инструмент. И то и другое может дать JSON верной формы. Защита не в том, чтобы воспроизвести этот текст. Сначала внесите инструмент в список разрешённых, прогоните вызов через схему, затем по grant выберите allow, deny или ask. Человек по-прежнему может отказать в вызове.
Чем эта статья отличается от других
| Зачем нужна схема | Кто отвечает за безопасность MCP | Эта статья | |
|---|---|---|---|
| Отвечает | Зачем выводу нужна форма | Кто отвечает, когда серверов много | Какой JSON прав ещё нужен после верной формы |
| Здесь не ищите | Ещё одно введение в схему | Кто в организации подписывает | Шаги атаки, которые можно повторить |
Остановить неверный allow в JSONNote
- 1Выбрать нужную операцию
Откройте JSON Schema. Переключите действовать на Проверьте с помощью Schema. Решение о правах — в первое поле, схему выше — во второе. decision может быть только allow, deny или ask. Каждый элемент grant — только read_inbox или send.
- 2Прогнать четыре плохих документа
Поставьте decision равным yes, argumentsValid — строке true, удалите decision или поставьте grant равным send_all. Все четыре не проходят. decision allow, reason ok и grant read_inbox проходит. После этого верните deny или ask и не вызывайте инструмент.
- 3Сравнить deny и allow
Откройте JSON Сравнить Diff. В первое поле — решение deny. Во втором поменяйте только decision на allow и reason на ok. grant остаётся read_inbox. Оба проходят схему. Отправку останавливает правило приложения, а не эта проверка формы.
Вопросы
Можно ли выполнять инструмент, как только схема прошла?
Нет. Схема доказывает только форму аргументов. Ещё проверьте, есть ли инструмент в списке и содержит ли grant эту способность. Если решение не allow, инструмент не вызывайте.
Если решение о правах прошло свою схему, можно ли поставить allow?
Не из этого одного. decision allow, reason ok и grant только с read_inbox — всё ещё верная форма. Приложение ещё должно увидеть toolAllowed true и нужную способность в grant. Если чего-то нет, это не allow.
Что здесь значат prompt injection и tool poisoning?
Это названия двух рисков, а не шаги, которые эта статья воспроизводит. Один — недоверенный текст, из-за которого модель предлагает вызов. Другой — недоверенное описание инструмента, из-за которого опасная способность выглядит обычной. Оба могут дать JSON верной формы. Их останавливают список разрешённых, схема и grant, а не переписывание этого текста.
Это та же статья, что про необходимость JSON Schema?
Нет. Те статьи объясняют, зачем выводу нужна форма. Эта начинается после того, как форма уже прошла: список разрешённых и grant всё ещё могут сделать decision равным deny или ask.
В чём разница между ask и deny?
deny значит не вызывать в этот раз. ask значит сначала показать аргументы человеку; если человек отказывает, всё равно не вызывать. Оба не равны allow. Человек в контуре — это способность отказать, записанная в спецификации инструментов MCP.
Итог
JSON Schema останавливает аргументы инструмента с неверной формой. Решение о правах останавливает вызов, форма которого уже верна, а способность — нет. Храните оба JSON, и у выполнения появляется ворота.
Решение: прохождение схемы значит только верную форму; сначала внесите инструмент в список; grant должен содержать эту способность; если decision не allow, не вызывайте; если decision равен allow, а grant недостаточен, смените на deny или ask, даже когда форма прошла.
Дальше: прогоните решение о правах через схему, затем проверьте, разрешает ли grant этот allow.