博客 • Agent Security

AI Agent 爲什麼不能只相信 JSON Schema?從 Schema Validation、Prompt Injection、Tool Poisoning 到權限控制完整解析

工具調用通過 JSON Schema,只說明參數的形狀對。發信、改文件、出網還要另一份 JSON:權限決定。decision 不是 allow,這次調用就停住。

輸出爲什麼要有形狀,見 AI 爲什麼需要 JSON Schema。連接很多工具服務器時誰負責安全,見 MCP 安全與權限。本篇只寫形狀通過之後的那份決定:允許名單、grant、allow / deny / ask。

先記住這一點:形狀對的調用,仍然可以是不該執行的調用。下面按「Schema 證明什麼 → 決定長什麼樣 → 通過仍要拒絕 → 三種情況停在哪」拆開。

Schema 證明什麼

JSON Schema 檢查類型、枚舉和必填。說明見 JSON Schema。它不證明這個會話可以發信,也不證明這個工具名是你審過的。

三個字段都是字符串。這樣的調用可以通過參數 Schema,仍然不該發出去。
{
  "tool": "send_email",
  "arguments": {
    "to": "ops@example.com",
    "subject": "status",
    "body": "ok"
  }
}

to、subject、body 都是字符串時,參數 Schema 只看見類型。它看不見這次會話的 grant 只有 read_inbox,沒有 send。

決定是另一份 JSON

MCP 的工具規範要求校驗輸入,並讓人能夠拒絕這次調用。說明見 MCP Tools。參數 JSON 通過之後,再寫一份決定。toolAllowed 表示這個工具名在你的允許名單上。grant 是這次會話已有的能力。decision 只能是 allow、deny 或 ask。

參數形狀合法,工具也在名單上,但 grant 只有讀信。decision 是 deny。
{
  "tool": "send_email",
  "argumentsValid": true,
  "toolAllowed": true,
  "grant": ["read_inbox"],
  "decision": "deny",
  "reason": "grant_missing"
}
第一欄貼決定,第二欄貼這份 Schema。操作改成用 Schema 校驗。
{
  "$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"]
    }
  }
}

這份 Schema 能攔住 decision 不在三個值裏、argumentsValid 不是布爾、缺 decision、以及 grant 裏出現名單以外的能力。它不能表達 allow 必須同時滿足 toolAllowed 和所需 grant。那三條留在 App 裏。

通過仍要拒絕

把上面的決定改成 decision 爲 allow、reason 爲 ok,grant 仍是 read_inbox。這份 Schema 會通過。通過不等於可以發信。App 裏的規則是:只有 argumentsValid 和 toolAllowed 都爲 true,並且 grant 含有 send,decision 纔可以是 allow。少一項,就改回 deny 或 ask,不調用工具。

App 裏的規則Schema 能看見嗎做不到時
allow 時 argumentsValid 必須爲 true看不見這層組合改成 deny,reason 爲 arguments_invalid
allow 時 toolAllowed 必須爲 true看不見這層組合改成 deny,reason 爲 tool_not_allowlisted
allow 時 grant 必須含有 send看不見這層組合改成 deny 或 ask,reason 爲 grant_missing

三種情況停在哪

工具說明上的註解只是提示。規範寫明,不可信來源的註解不能當成已經執行的安全控制。說明見 工具註解不是合同。真正停住調用的是你的 Schema 和這份決定。

情況形狀可能怎樣停在哪
參數形狀不對Schema 失敗不寫 allow,不調用
工具不在允許名單參數仍可能合法toolAllowed 爲 false,decision 爲 deny
會話沒有這項能力參數合法,工具也在名單上grant 不含所需能力,deny 或 ask

不可信的文字讓模型提出一次調用,或不可信的工具說明讓一個危險能力看起來像普通工具,這兩件事都可能交出一份形狀合法的 JSON。防護不是去復現那份文字,而是:工具先入允許名單,調用再過 Schema,最後用 grant 決定 allow、deny 或 ask。人仍然可以拒絕。

和另外幾篇怎麼分工

爲什麼需要 SchemaMCP 誰負責安全本篇
回答輸出爲什麼要有形狀很多服務器時誰負責形狀通過之後還要哪一份權限 JSON
不要在這裏找再講一遍 Schema 入門組織上誰簽字一份能照着做的攻擊步驟

在 JSONNote 裏擋住 allow

  1. 1
    選對操作

    打開 JSON Schema。操作選「用 Schema 校驗」。第一欄貼權限決定,第二欄貼上面的 Schema。decision 只能是 allow、deny 或 ask。grant 裏的每一項只能是 read_inbox 或 send。

  2. 2
    撞四條壞數據

    decision 寫成 yes,或 argumentsValid 寫成字符串 true,或刪掉 decision,或 grant 寫成 send_all。這四條都會失敗。把 decision 改成 allow、reason 改成 ok、grant 仍是 read_inbox,會通過。通過之後仍要改回 deny 或 ask,不調用工具。

  3. 3
    對比 deny 和 allow

    打開 JSON 對比 Diff。第一欄貼 deny 那份決定,第二欄只把 decision 改成 allow、reason 改成 ok。grant 仍是 read_inbox。兩份都能通過 Schema。擋住發信的是 App 規則,不是這次形狀檢查。

常見問題

Schema 通過就可以執行工具嗎?

不行。Schema 只證明參數形狀。還要看工具是否在允許名單,以及 grant 裏有沒有這項能力。決定不是 allow,就不調用。

權限決定的 Schema 通過,就可以寫成 allow 嗎?

不可以只憑這一點。decision 爲 allow、reason 爲 ok、grant 只有 read_inbox,形狀仍然合法。App 還要確認 toolAllowed 爲 true,並且 grant 含有這次需要的能力。少一項就不是 allow。

Prompt injection 和 tool poisoning 在這裏指什麼?

它們是兩類風險的名字,不是本篇要復現的步驟。一類是不可信文字讓模型提出調用。一類是不可信的工具說明讓危險能力看起來像普通工具。兩類都可能交出形狀合法的 JSON。停住它們的是允許名單、Schema 和 grant,不是把那段文字再寫一遍。

和「爲什麼需要 JSON Schema」是同一篇嗎?

不是。那幾篇說明輸出爲什麼要有形狀。本篇從形狀已經通過寫起:允許名單和 grant 仍然可以讓 decision 變成 deny 或 ask。

ask 和 deny 有什麼差別?

deny 是這次不調用。ask 是先給人看參數,人拒絕就仍然不調用。兩條都不是 allow。人在迴路裏,是 MCP 工具規範裏寫明的拒絕能力。

總結

JSON Schema 把工具參數停在錯誤的形狀上。權限決定把形狀已經正確的調用停在錯誤的能力上。兩份 JSON 都留下,執行纔有門。

直接結論:Schema 通過只說明形狀對;工具先入允許名單;grant 裏要有這次能力;decision 不是 allow 就不調用;allow 但 grant 不夠,形狀通過也要改成 deny 或 ask。

下一步:先讓權限決定通過 Schema,再看 grant 是否允許這次 allow。

返回博客