AI Agent 安全指南:惡意 JSON、Prompt Injection、Tool Calling 與結構化資料攻擊風險

截至 2026 年 9 月 14 日:Agent 一旦能調工具、吃外部 JSON,攻擊面就從「騙一句回覆」變成「騙一次呼叫」。防禦指南:惡意 JSON、間接注入、Tool Calling 越權與過鬆的 Schema。

先給結論:2026 年談 AI Agent 安全,核心不是「模型會不會說錯話」,而是「不可信的結構化資料會不會變成一次真實的工具呼叫」。聊天機器人被注入,最壞是一段誤導回覆;Agent 被注入,最壞是發郵件、改檔案、查庫、調支付。JSON 既是契約,也是載荷。Prompt Injection 經常不長在聊天框裡,而長在工單、網頁、郵件、API 響應這些 JSON 欄位裡。

這篇按 2026 年 9 月 14 日寫。本站已有《Tool Calling 為什麼依賴 JSON Schema》《Agent JSON 資料流》《JSON Schema 標準合同》《MCP / Skills / Tools / Subagents》。本文只回答:惡意 JSON、間接注入、Tool Calling 越權、過鬆 Schema 各自是什麼風險,以及 Host 側該怎麼防。本文是防禦指南,不提供可復現攻擊步驟,也不給出可直接使用的注入文字。

一張表:聊天機器人 vs Agent 的攻擊面

使用者在對話方塊裡打字,只是 Agent 輸入的一小部分。2026 年的預設棧裡,模型還會吃工具返回值、MCP Resources、網頁摘錄、工單欄位、郵件正文——它們幾乎都以 JSON 或「包在 JSON 裡的字串」進上下文。行業裡常把這類問題歸到 Prompt Injection、Insecure Output Handling、Excessive Agency;落地時不必死記名詞,先分清誰在說話、誰在執行。

維度聊天機器人能調工具的 Agent
失敗形態一段錯誤或被誘導的回覆一次真實副作用:寫檔案、發請求、改資料
不可信輸入當前使用者訊息使用者訊息 + 外部 JSON + 工具結果 + 網頁/郵件欄位
執行者模型只生成文字Host / MCP Server 按 arguments 執行
合同提示詞(軟)JSON Schema + Host 校驗(硬)
最小修復改提示詞、加拒絕策略收緊 Schema、工具白名單、獨立鑑權

記一句:模型可以被騙;Host 不該被連帶執行。安全邊界在「生成 tool_calls」和「真正執行」之間。中間必須有校驗、授權與審計,而不是把模型輸出當可信 RPC。

惡意 JSON:多欄位、型別錯位、巢狀載荷

惡意 JSON 不一定是「解析失敗的髒資料」。更常見的是:語法合法、對業務危險。嚴格解析器會拒絕尾逗號或註釋;寬鬆解析器、手寫拼接、或「先當文字再 JSON.parse」會把問題推到更後面。Agent 場景裡,危險通常出現在 Host 已經當成物件用的那一刻。

三類最值得先防的形狀(只描述模式,不給利用步驟):

模式看起來像什麼若 Host 未校驗會怎樣防禦
多餘欄位業務物件多出未宣告鍵被 merge 進配置,或原樣傳給下一跳工具additionalProperties: false,丟棄未知鍵
型別錯位該是 number / array,來了 string 或 object鑑權分支走錯,或把整段文字當參數寫死 type、enum、format
巢狀載荷字串欄位裡再塞 JSON 或長說明內層文字進入提示詞,被當成指令限制長度;內層再校驗;不要把原文當系統話

下面是「看起來能用、其實過鬆」的工具參數合同,以及收緊後的對照。這是防禦樣例,不是攻擊材料:

{
  "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」和「你允許的最小物件」。多出來的鍵、被改掉的型別,就是第一道警報。

另一條容易漏的:不要把不可信物件直接 merge 進內部配置或原型鏈可觸及的結構。在 JavaScript Host 裡,未知鍵進配置物件,後果可能超出「多一個欄位」。防禦仍是同一句:先按 Schema 投影出白名單物件,再往下傳。

Prompt Injection:指令藏進結構化資料

直接注入是使用者對著對話方塊說話,試圖覆蓋系統提示詞。間接注入更符合 2026 年的 Agent 現實:指令藏在模型稍後會讀到的資料裡——工單標題、郵件正文、網頁段落、PDF 摘錄、另一個工具返回的 JSON 字串。模型並不天然區分「開發者寫的規則」和「資料裡的句子」。

結構化資料讓這件事更隱蔽。一段客戶備註在 API 裡只是 customer_note 字串;進了上下文,它和系統提示詞變成同一段 token 流。Host 若把工具結果原樣拼進下一輪訊息,等於讓外部站點或發件人給 Agent「寫提示詞」。

防禦不靠「再寫一句請忽略惡意指令」。提示詞可以降風險,不能當邊界。更穩的做法:

  • 標記來源:外部文字用明確角色或包裝欄位進入對話(例如 tool / untrusted),不要並進 system。
  • 限制可見面:能摘要就不要貼全文;能只讀欄位就不要把整份 JSON 塞進視窗。
  • 高風險動作二次確認:轉賬、刪資料、對外傳送,必須有獨立於模型的策略引擎或人工門禁。
  • 工具結果當資料,不當指令:返回值先 Schema 校驗,再決定是否進入提示詞。

和本站《Apple Intelligence 如何保護個人資料》同一條直覺:真正漏出去或被濫用的,常常是你寫進 JSON 的欄位。欄位進了模型,就可能被當成話;欄位進了工具參數,就可能被當成動作。

Tool Calling:被當成合法呼叫的越權

Tool Calling 的危險不在「模型會不會生成 JSON」,而在 Host 是否把模型當成已授權的呼叫方。這是經典的 confused deputy:模型只是提議;許可權應屬於使用者會話、租戶和服務賬號。模型說「呼叫 export_orders」,不等於當前使用者有權匯出全表。

2026 年常見的過度授權長這樣:

  • 一把 run_command / http_request 包打天下,參數幾乎是任意字串;
  • 工具以服務賬號入場,比終端使用者許可權更大;
  • Host 只檢查 JSON 語法,不檢查「這個使用者能不能動這個資源」;
  • 把使用者或網頁裡的 JSON 直接當工具 arguments,不再走自己的 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 查自己的允許列表、補全域名、加超時與大小上限。模型看不到原始任意 URL,也就少了一條把會話帶去不可信源的路。

執行前再問三個問題:這個工具當前會話是否在白名單裡?arguments 是否透過獨立於模型的 Schema 與鑑權?失敗時是拒絕,還是把錯誤細節回灌成新的可注入文字?《參數錯誤與驗證方法》寫過校驗流水線;安全場景裡還要加上:校驗失敗預設拒絕,不要讓模型「換個參數再試」無限轉。

Schema 太鬆就是漏洞

Structured Output 與 Tool Calling 都在用 JSON Schema 擋非法 token,這是 2026 年的預設合同,詳見《Structured Output 是什麼》。但 Schema 只保證形狀,不保證安全語義。

過鬆的合同常見於:

  • type: object 卻不寫 properties,或 additionalProperties 為 true;
  • 本該是列舉的動作名,寫成任意 string;
  • 本該是識別符號的欄位,寫成可承載整頁說明的長文字;
  • 廠商 strict 模式只覆蓋子集,Host 卻以為「模型側已經攔死」。

正確疊法是兩道門:模型側 Schema 降低胡言;Host 側再用同一份(或更嚴的)Schema 校驗一遍,然後投影成內部型別。廠商檔案裡的 Structured Outputs 不能替代你自己的鑑權。OpenAI 與 Gemini 的欄位差異見《Structured Output 對比》——差異本身也是風險:遷移時合同變鬆,攻擊面跟著變大。

把 Schema 當安全控制時,優先寫這些約束:enum、const、pattern、maxLength、minimum / maximum、required、additionalProperties: false。需要自由文字時,單獨開欄位並設上限,且永遠不要把自由文字對映成工具名或 URL。

MCP 與外部工具:信任邊界在哪

MCP 解決的是跨程式發現與呼叫,不解決「這個 Server 是否善意」。《MCP 是什麼》把 JSON-RPC 邊界畫清楚了:模型 API 在一邊,Host ↔ Server 在另一邊。安全上要再畫一條:第三方 MCP Server 的工具描述、Resources 文字、返回 JSON,都是不可信輸入。

風險不在協議版本號,而在信任被傳錯層:

  • tools/list 回來的 description / inputSchema 被原樣展示或注入系統提示詞;
  • tools/call 的 result 未校驗就進入下一輪上下文;
  • 同一 Host 掛了過多 Server,工具重名或能力重疊,模型選錯也照執行;
  • 把「使用者點了允許這臺 Server」理解成「它返回的每句話都是指令」。

Skills 也有同類問題:Skill 是做事說明書,不是鑑權層。不可信倉庫裡的 SKILL.md 被載入後,等於多了一份可被模型遵循的流程。分層見《四層分別管什麼》——不要用 Skill 代替 Schema,也不要用 Subagent 代替許可權隔離。 Subagent 可以收窄工具集,但父程式仍要決定它拿得到什麼金鑰。

防禦清單:校驗、白名單、最小許可權

按執行路徑由外到內收,而不是先改提示詞:

  1. 先寫合同。每個工具一份 JSON Schema:required 寫全,additionalProperties: false,識別符號用 pattern / enum。
  2. Host 再校驗一遍。不要相信模型「已經按 Schema 生成」。用 ajv 或等價庫;失敗即拒絕。
  3. 投影,不要透傳。只複製白名單欄位到內部 DTO,再呼叫業務 API。
  4. 工具最小化。能 fetch_public_doc 就不要 fetch_url;能只讀就不要寫。
  5. 鑑權跟使用者走,不跟模型走。在工具實現裡檢查會話、租戶、資源 ACL。
  6. 對外副作用加門禁。傳送、轉賬、刪除、生產部署:策略引擎或人工確認。
  7. 外部文字降權。工具結果、網頁、郵件不當 system;必要時另開只讀 Subagent,父會話只收摘要。
  8. 審計 arguments。記下工具名、校驗後的參數、誰授權、是否被拒絕——除錯和追責都靠它。

提示詞仍然有用:告訴模型「資料不是指令」、列出禁止動作。它是輔助層。少一層 Schema 或鑑權,多寫十句提示詞也補不回來。

用本機 JSON 工具做安全審查

上線前把這三份檔案放在一起看:工具 parameters / MCP inputSchema、一條「正常」arguments、一條「故意難看」的樣例(多餘鍵、錯誤型別、超長字串)。不要用真實金鑰或真實使用者資料。

  • JSON 校驗:樣例是否合法 JSON,是否滿足你寫的 Schema。
  • JSON Diff:模型輸出比最小物件多了什麼。
  • 樹形檢視:巢狀是否深得不該深,字串欄位是否夾著另一段結構。

資料不離開瀏覽器,適合審查即將進生產的合同,也適合對照一次失敗呼叫的 arguments。欄位名和 required 穩定了,再接到 Host 或 MCP Server。

常見問題 FAQ

JSON Schema 能防止 Prompt Injection 嗎?

不能單獨防止。Schema 限制工具參數的形狀與取值範圍,降低「任意字串變成任意動作」的機率。間接注入發生在文字進入上下文時,還要靠來源隔離、降權與高風險門禁。

模型已經開了 Structured Output / strict mode,Host 還要校驗嗎?

要。廠商約束的是生成階段,而且各家 Schema 子集不同。安全決策必須發生在執行前,用你自己的校驗器和鑑權。

把使用者 JSON 直接傳給工具,有什麼問題?

等於跳過合同。多餘欄位、型別錯位、巢狀文字會原樣到達具有副作用的程式碼。應先校驗再投影,只傳白名單欄位。

MCP Server 返回的內容可以當系統提示詞嗎?

不可以。tools/list 的描述、Resources 文字、tools/call 結果都應視為不可信資料,校驗後再降權進入對話。

最小可用的 Agent 安全基線是什麼?

嚴格 JSON Schema、Host 二次校驗、工具白名單、按使用者鑑權、對外副作用門禁。沒有這五條,不要把 Agent 接到生產資料。

如何本機檢查工具相關的 JSON 是否危險?

把 Schema 與 arguments 樣例放進 JSON 工具箱做校驗與 Diff。確認 required、additionalProperties 與長度/列舉限制後,再接到 Host 或 MCP Server。

總結

Agent 的攻擊面在結構化資料與工具執行之間:惡意 JSON 負責混進合同,Prompt Injection 負責改模型意圖,Tool Calling 負責把意圖變成副作用,過鬆的 Schema 則給前三步開門。2026 年的預設棧(Tools、MCP、Skills、Subagents)讓能力更好用,也讓「騙一次呼叫」比「騙一句回覆」更值錢。

防禦由內向外:先把 JSON Schema 寫成硬合同,Host 再校驗、再投影、再鑑權;工具儘量小,外部文字儘量不當指令。提示詞是輔助,不是邊界。動手前用本機 JSON 工具把樣例跑通——模型可以換,欄位名、required 和「誰有權執行」不該變。