← 所有文章

你的智能体有两个不可信输入

来自指南: Claude Code Comprehensive Guide

传统程序只有一条信任边界:用户交给它的输入。你在那条边上做校验,事情基本就结束了。智能体悄悄又加了两条,而围绕智能体建起来的工具大多只把其中一条当真。

第一个新输入是模型写的代码,由你的进程运行。第二个是模型读到的工具输出,然后据此行动。两者都从你控制不了的地方进入你的系统,一个来自语言模型的采样分布,一个来自第三方的服务器,而且两者一到达就被当作可信的。眼下智能体安全之所以让人觉得没有着落,是因为业界对第一个输入已经有了可信的答案,却还在假装第二个输入是同一个问题。它不是。

核心要点

  • 智能体有两个不可信输入,不是一个。 由你的运行时执行的模型生成代码,以及由你的模型摄入并据此行动的工具/服务器输出。它们以不同方式失效,需要不同的防御,把它们混为一谈正是人们感觉自己有沙箱却依然可被利用的原因。12
  • 不可信代码的执行终于有了可信的隔离答案。 Simon Willison 的 micropython-wasm 在一个带内存和 CPU 限制、可控文件与网络访问的 WebAssembly 沙箱里运行模型写的 Python,这些正是一个真正的沙箱需要的特性,建立在一个“在浏览器里经受了近十年考验”的基底之上。1
  • 工具投毒不是代码执行问题,所以代码沙箱对它毫无作用。 藏在 MCP 工具描述里的恶意指令“对用户不可见但对 AI 模型可见”,即便被投毒的工具从未运行,它们也能劫持智能体。23
  • 最深层的问题是连接时与运行时之间的信任缺口。 工具描述只在批准时审查一次;工具响应则直接流入模型的上下文,没有任何对等的检查,而恶意服务器可以在你批准之后修改描述。2

两个输入,两种失效方式

把两个输入并排放着看,因为不对称就是全部要点。

当模型写了代码而你去 exec() 它时,失效方式是代码对你的机器做了什么:读了不该读的文件、打开一个套接字、让 CPU 空转到永远、破坏状态。爆炸半径是你的进程以及它能触及的一切。防御古老且被充分理解:隔离。把代码放到一个碰不到要紧东西的地方运行。

当模型读到工具输出并据此行动时,失效方式是那些文字改变了模型决定要做的事。经典意义上没有任何东西在你的主机上执行;相反,作为“数据”到达的文本被当成了“指令”来解释,而握着你凭据的智能体把它执行了。爆炸半径是你的智能体被授权去做的一切。再多的进程隔离都帮不上忙,因为智能体本来就应该能读那个工具并据此行动。漏洞利用走的是合法路径。

代码沙箱是围着第一个输入的一堵墙。它是必要的,而且终于变好了。它对第二个输入也完全沉默。

终于有了沙箱的那个输入

2026 年 6 月 6 日,Simon Willison 发布了 micropython-wasm,一个在 WebAssembly 沙箱里运行 Python 的 alpha 包,此前他多年尝试的各种方案都不太合适。1 动机正是第一个不可信输入:他想在自己的 Datasette Agent 里运行 LLM 生成的代码而不危及主机,用他的话说,“一个有 bug 或恶意的插件可能弄坏一切或泄露私密数据”。1

它值得关注,是因为它实际满足的那份清单,而那份清单就是可用沙箱的定义:

  • 内存和 CPU 限制,这样模型写出的 while True: s += "x" 就无法耗尽主机。CPU 用 wasmtime 的“fuel”机制来限制,Willison 对此很坦率,指出其单位“很难推理”。1
  • 可控的文件与网络访问,默认拒绝而不是默认开放。
  • 一个干净的隔离模型:代码在一个请求/应答队列后面的独立线程里运行,WASM 解释器阻塞在一个宿主函数上,直到被交给工作,所以不可信代码从不与主机共享地址空间。1
  • 一个有过往记录的基底。 他选择 WebAssembly 是因为它“从一开始就被设计来支持我在意的所有特性,并且在浏览器里经受了近十年的考验”,选择 MicroPython 则是因为面向浏览器的 Pyodide 不支持服务器端。1

这种坦率是好的安全工作的标志。Willison 以 alpha 形式发布它,并写道他“还没准备好把它推荐给任何不愿承担重大风险的人”。1 这就是证据门的姿态:只声称你已验证的,一分不多。重点不在于这一个包就是答案;而在于不可信代码的答案的形状现在清楚且可复现了:一条带资源限制和默认拒绝 I/O 的真正隔离边界。第一个输入正在变成一个已解决的工程问题。

陷阱在于相信解决了它你就安全了。

没有沙箱的那个输入

第二个输入不运行代码,所以墙什么也做不了。2025 年 4 月 1 日,Invariant Labs 披露了工具投毒攻击:“恶意指令被嵌入 MCP 工具描述之中,对用户不可见但对 AI 模型可见。”2 这种不对称就是漏洞本身:“AI 模型看到完整的工具描述,包括隐藏的指令,而用户在他们的 UI 里通常只看到简化版本。”2 一个在 UI 里写着“把两个数相加”的工具,可以在模型实际消费的描述里携带一条指令:读取 ~/.ssh/id_rsa 并通过一个隐藏参数把它外泄。2 OWASP 现在把这归类为一种有名字的攻击类别。3

有两个属性让它以隔离够不着的方式变得恶劣:

它不需要工具被调用就能触发。 被投毒的指令住在描述里,而模型在考虑工具集时就会摄入它,所以即便函数从未执行,攻击也能落地。3 没有代码事件可以拿去沙箱化。

信任一次授予,之后被滥用。 工具描述在连接时审查,也就是你批准一个服务器的时候。之后响应在运行时直接流入模型的上下文,没有任何对等的检查,而且,按 Invariant 的“抽地毯”发现,“恶意服务器可以在客户端已经批准之后修改工具描述”。2 你的一次性批准保护不了任何持续的东西。

那个经典演示不是假设。研究者给一条 GitHub issue 投了毒,注入了文本;当一位开发者让助手审查开放的 issue 时,智能体摄入了那条 issue,遵循了隐藏指令,用开发者的令牌读取了私有仓库,并把内容泄露进一个公开的 pull request。3 每一个单独的动作都是被授权的。智能体做的正是智能体该做的事。这就是为什么代码沙箱在这里无关紧要:没有任何不可信的东西执行了;只是不可信的文本在一个握有真实凭据的进程里被提升成了指令。这和 Ken Thompson 在《反思对信任的信任》里留下的教训是同一个:危险不在于你检查的代码,而在于你未经检查就给出的信任。

防御第二个输入

如果隔离是针对代码的防御,那么针对工具输出的防御就是把数据与指令之间的边界当作真实存在的,在运行时,每一次,也就是连接时批准模型跳过的那件事。微软自己的 MCP 指南也落在同一个地方,建议用能检测并过滤注入指令的“提示防护”,外加 spotlighting,即“在系统消息中加入分隔符,明确标出输入文本的位置”,让模型能区分内容与命令。4 具体来说:

  • 在运行时重新检查描述,而不只是在批准时。 固定住你批准的那份工具定义,并检测服务器的描述何时在你脚下发生了变化。抽地毯之所以能成功,只是因为没人看第二眼。2
  • 显式标记数据/指令边界。 工具响应是数据,不是指令,而实用的做法就是 spotlighting:在上下文里给工具输出加上分隔符,从结构上告诉模型,这是拿来推理的内容,而不是要遵循的命令。4 它与 MCP 工具的动作级授权配合,这样即便一个被劫持的智能体也够不到那些高后果的调用。
  • 对被授权的路径应用最小权限。 GitHub 那次利用之所以成功,是因为一个智能体同时握有“读私有仓库”和“开公开 PR”,而最小权限正是微软点名的对抗这类攻击的基础卫生措施。4 Joanna Rutkowska 的通过分区实现安全是架构层面的同一种本能:假定一个域已被攻陷,并确保它够不到其他域。读 issue 的智能体不应该是握着外泄路径的那个智能体。
  • 盯住出口流量,因为外泄才是收益所在。 这些攻击大多终结于同一步:数据离开。一个静默出口视角能在注入溜过前门之后仍然抓住后果。

这些没有一个是墙。它们是智能体本该走的那条路上的门,这是更难、更不令人满足的那种安全,也是第二个输入真正需要的那种。

我从中取走的

我要留下的心智模型是:“我的智能体有沙箱吗?”是个错误的问题,因为它悄悄假定只有一条边界要守。有两条。我的模型写的代码需要一堵墙,多亏了 Willison 这样的工作,砌那堵墙正在变成一项有清楚规格的常规工程任务。我的模型读的工具输出需要的是完全不同的东西:一个从不让数据悄悄变成指令的运行时,以及一个紧到被劫持的智能体也做不了多少事的授权模型。把它们混为一谈,就是你如何一边觉得受到了保护,“看,代码跑在 WASM 里”,一边通过那个没人装门的输入大敞四开。

更深的连续性在于,智能体与其说发明了一个新的漏洞类别,不如说给一个老漏洞添了新的栖身之处。不可信输入向来是安全的根源;智能体时代只是给它开了两扇新门,一扇你能用隔离锁上,一扇你只能靠纪律盯着。既要砌墙也要装门。在一个有两个输入的系统上,只有一堵墙,是一份感觉像完整的半份防御。

常见问题

AI 智能体的两个不可信输入是什么?

由你的运行时执行的模型生成代码,以及由你的模型读取并据此行动的工具或服务器输出。前者能直接损害主机;后者能用智能体已经持有的凭据和权限劫持智能体的决策。它们需要不同的防御:代码用隔离,工具输出用运行时的数据/指令分离和严格的授权。12

代码沙箱能防住 MCP 工具投毒吗?

不能。工具投毒不在你的主机上执行代码,它把指令嵌入一个工具的描述里,让模型把它当作可信的,而且即便工具从未被调用也能触发。23 沙箱隔离的是执行,所以对一个搭乘智能体合法、被授权动作的攻击毫无作用。

什么是工具投毒攻击?

由 Invariant Labs 在 2025 年 4 月披露,这是一种“恶意指令被嵌入 MCP 工具描述之中,对用户不可见但对 AI 模型可见”的攻击。2 因为模型看到完整描述而用户只看到简化的 UI 标签,隐藏的指令,比如读取 SSH 密钥并外泄,就能劫持智能体。OWASP 现在把它作为一种有名字的攻击类别来追踪。23

什么是连接时与运行时的信任缺口?

工具描述只审查一次,在你批准一个服务器的时候(连接时)。之后工具响应在运行时流入模型的上下文,没有任何对等的检查,而且恶意服务器可以在批准之后修改描述(“抽地毯”)。2 修复办法是在运行时验证,而不只是在批准时。


来源


  1. Simon Willison,“Running Python code in a sandbox with MicroPython and WASM,” simonwillison.net,2026 年 6 月 6 日。介绍 alpha 版 micropython-wasm 包和 datasette-agent-micropython 插件;动机是“一个有 bug 或恶意的插件可能弄坏一切或泄露私密数据”;沙箱清单(通过 wasmtime “fuel” 实现内存/CPU 限制、可控文件/网络访问、独立线程的请求/应答隔离);选择 WebAssembly 的理由是它“从一开始就被设计来支持我在意的所有特性,并且在浏览器里经受了近十年的考验”;以及他“还没准备好把它推荐给任何不愿承担重大风险的人”的警示。 ↩↩↩↩↩↩↩↩↩

  2. Luca Beurer-Kellner 与 Marc Fischer,“MCP Security Notification: Tool Poisoning Attacks,” Invariant Labs,2025 年 4 月 1 日。原始披露:“当恶意指令被嵌入 MCP 工具描述之中、对用户不可见但对 AI 模型可见时,就发生了工具投毒攻击”;可见性缺口(“AI 模型看到完整的工具描述,包括隐藏的指令,而用户在他们的 UI 里通常只看到简化版本”);“恶意服务器可以在客户端已经批准之后修改工具描述”的“抽地毯”发现;以及读取 ~/.cursor/mcp.json 和 ~/.ssh/id_rsa 的被投毒 add 示例。 ↩↩↩↩↩↩↩↩↩↩↩↩↩

  3. “MCP Tool Poisoning,” OWASP Foundation。把工具投毒归类为一种有名字的攻击类别:藏在工具元数据里、“即便被投毒的工具从未被调用”也会影响智能体的指令,以及那个 GitHub MCP 示例,一条被投毒的 issue 引导智能体通过开发者的令牌读取私有仓库并把它们泄露进一个公开的 pull request。 ↩↩↩↩↩↩

  4. Sarah Young 与 Den Delimarsky,“Protecting against indirect prompt injection attacks in MCP,” Microsoft Developer Blog,2025 年 4 月 28 日。建议使用“利用先进的机器学习算法和自然语言处理来检测并过滤恶意指令”的“提示防护”、“在系统消息中加入分隔符,明确标出输入文本的位置”的 spotlighting、对 MCP 组件的供应链验证,以及最小权限之类的基础卫生措施。 ↩↩↩

相关文章

你的Agent沙箱只是一个建议

一名攻击者提交了一个GitHub issue,并在Cline的下一个版本中植入了恶意软件。Agent沙箱在三个层面上失效。以下是真正有效的防御方法。

14 分钟阅读

MCP服务器是新的攻击面

50个MCP漏洞,60天内30个CVE,其中13个严重。工具使用协议是无人审计的攻击面——这里是分类与修复方案。

6 分钟阅读

Ralph循环:我如何在夜间运行自主AI代理

我构建了一个使用停止钩子、生成预算和文件系统记忆的自主代理系统。以下是失败经验以及真正能交付代码的方法。

8 分钟阅读