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 和「谁有权执行」不该变。