几周前,一个团队在生产环境遇到了这样的事:Agent 在执行"读取最新销售报告"的指令时,突然开始向外网发送内部文件。排查后发现,问题出在一个第三方 MCP 插件上——这个插件被植入了隐藏的指令,在返回文件内容时附带了一段注入文本,Agent 误以为这是用户的新指令,转而执行了数据外传。
这不是个例。随着 MCP(Model Context Protocol)成为 AI Agent 连接外部工具的事实标准,Agent 的工具供应链正在成为新的攻击面。一个 MCP 插件就是一个可执行代码包,它由第三方开发,运行在 Agent 的进程中,拥有文件系统、网络和 API 的访问权限。当这个插件被攻陷,整个 Agent 就变成了攻击者的代理。
工具供应链与传统软件供应链的差异
传统软件供应链安全的核心问题是:你依赖的库是否包含已知漏洞?你能否及时打补丁?CVE 扫描、SBOM 管理、依赖更新——这些工具链已经相当成熟。
AI Agent 的工具供应链引入了两个新的维度。
第一,工具不仅是代码,还是指令载体。一个 MCP 插件的 read_file 工具返回的文本内容,会被直接送入 LLM 的上下文窗口。这意味着工具的返回值本身可以携带指令——如果攻击者能控制这个返回值,他就能间接控制 Agent 的下一个决策。这不是传统意义上的代码执行漏洞,而是"数据即指令"的架构级风险。
第二,工具的权限模型是扁平的。Agent 进程加载了三个 MCP 插件,每个插件都声明了各自的工具——一个可以读文件,一个可以发邮件,一个可以执行 SQL。但在 Agent 的视角里,这些工具只是列表中的条目,它没有概念区分"这个工具来自哪个插件,安全等级如何"。Agent 调用工具的决策完全基于 LLM 对工具描述的语义理解,而不是基于预先定义的权限边界。
工具中毒攻击的反应链
理解 MCP 插件如何成为攻击入口,需要看清楚攻击执行的完整链路。
一个典型的工具中毒攻击分三步走:
第一步,攻击者选择一个目标 MCP 插件。这个插件可以是开源项目,也可以是通过 npm 或 PyPI 分发的包。攻击方式包括:直接提交恶意 PR 被合并到上游、创建名字相似的恶意变体插件(typosquatting)、或者攻破插件作者的发布凭证后植入后门。
第二步,被污染的插件在正常功能之外,植入了一个"触发器"。这个触发器不一定是一个独立的工具——它更可能是对正常工具返回值的篡改。比如一个 RSS 阅读插件,它在返回文章列表时,在第一条内容的末尾附加了一段隐藏的提示注入文本。这段文本用白色字体写在 HTML 注释中,或者用零宽字符编码,用户端看不见,但 LLM 会读到。
第三步,当 Agent 调用这个工具并读取返回值时,LLM 将注入文本解释为新的指令。由于当前对话上下文没有区分"用户输入"和"工具返回数据"的边界,LLM 会尝试执行这个指令。结果可能是:调用另一个工具发送敏感数据、修改系统配置、或者执行一个授权的操作但指向攻击者指定的目标。
这个链路的可怕之处在于,攻击者不需要直接攻破 Agent 的宿主环境。他只需要控制一个 MCP 插件返回的数据,就能让 Agent 自己完成后续的攻击动作。Agent 的工具调用权限越广,攻击半径就越大。
当前 MCP 插件的安全现状
MCP 生态目前缺乏传统软件供应链的治理基础设施。
没有统一的签名机制。大部分的 MCP 插件以源代码形式直接加载,没有代码签名、没有哈希校验、没有发布者身份验证。一个 npm 包的名字可以随意注册,mcp-server-google-calendar 和 mcp-server-goog1e-calendar 之间没有可信的区分机制。
没有权限声明。一个 MCP 插件声明了哪些工具,它就拥有这些工具对应的全部能力。没有"这个插件只应该访问 /tmp/ 目录下的文件"这样的约束声明。如果插件声明了 read_file 和 write_file,它就能读写整个文件系统——即使它的实际功能只是一个天气查询工具。
没有运行时隔离。多数实现中,MCP 插件作为一个子进程或线程运行在 Agent 的进程中,共享同样的文件系统、网络栈和凭据。一个插件被攻陷,所有插件共享的进程上下文也随之暴露。
这些问题的根源在于:MCP 协议设计之初,关注的是"工具如何被发现和调用"的交互协议,而不是"工具如何被安全地执行"的信任模型。协议本身并不禁止上述行为,它只是没有定义安全层的契约。
构建工具供应链的安全架构
要在生产环境中安全地使用 MCP 插件,需要在 Agent 架构中引入多层防御。
第一层:插件准入与验证
在 Agent 加载任何 MCP 插件之前,应该有一个准入检查层。这个层做三件事:
验证插件来源。插件的包名、发布者身份、版本号需要与可信来源对照。对于内部开发的插件,可以建立私有的插件注册表;对于第三方插件,需要维护一个经过审核的插件清单,而不是直接从 npm 或 GitHub 拉取最新版。
检查插件的工具声明。一个插件声明了哪些工具、每个工具的参数和返回值类型,这些信息应该在加载时被审计。如果发现工具声明与实际功能不符——比如一个"天气查询"插件声明了 write_file 工具——应该拒绝加载。
扫描依赖的已知漏洞。MCP 插件本身是一个软件包,它的依赖链可能包含已知漏洞。在插件加载前,对其依赖进行 CVE 扫描是必要的。
第二层:运行时隔离与最小权限
每个 MCP 插件应该运行在独立的沙箱中,而不是共享 Agent 的进程空间。
容器化是最直接的方案。每个插件在独立的 Docker 容器中运行,通过文件系统挂载、网络策略和 capabilities 控制来限制它的行为。一个只需要 HTTP 访问的插件,它的容器不需要挂载宿主文件系统,也不需要网络以外的 capabilities。
更轻量的方案是使用 gVisor 或 Firecracker 微 VM。这些方案的开销比完整容器更低,但提供了类似的安全边界。对于需要低延迟的 Agent 场景,微 VM 的启动时间(几百毫秒)是可以接受的代价。
权限声明文件应该成为 MCP 插件的标准配置。一个插件在发布时,需要声明它需要访问哪些资源——比如只读的 /tmp/reports/ 目录、只允许 GET 的 HTTP 端点、不允许执行系统命令。Agent 的运行时根据这个声明文件来配置沙箱的权限边界,而不是由插件代码自行决定。
第三层:工具调用的策略执行网关
在 Agent 的推理循环和 MCP 插件之间,插入一个策略执行网关(Policy Enforcement Gateway)。这个网关拦截每一次工具调用和工具返回,它的职责包括:
输入校验。检查工具调用的参数是否符合预期。一个 read_file 工具的路径参数不能包含 .. 或指向敏感目录。一个 send_email 的收件人地址应该被限制在批准列表内。
输出过滤。检查工具返回的内容是否包含潜在的注入载荷。这不是一个简单的字符串匹配问题——攻击者可以使用编码、模板语言、或者语义诱导来绕过关键字过滤。更可靠的方案是对返回内容进行结构化抽离,只提取预期的数据字段,丢弃所有装饰性文本或元数据。
权限边界检查。每次工具调用都经过一个策略引擎——可以是 OPA 或自定义规则引擎——来判定这次调用是否在允许范围内。策略引擎的规则应该基于"默认拒绝"原则:没有明确放行的工具调用,一律拒绝。
第四层:高风险操作的审批机制
不是所有工具调用都需要实时审批,但也不是所有调用都可以自动执行。一个合理的分级策略是:
只读操作(读文件、搜索、查询天气)自动放行。这些操作的风险低,审批的延迟成本高于安全收益。
写入操作(创建文件、发送消息、更新数据库)需要事中确认。Agent 可以执行,但结果在发送给外部系统前需要经过一个短暂的"确认窗口"——有些实现称之为"延迟提交"模式。
执行操作(运行 Shell 命令、执行 SQL、调用删除 API)需要事前审批。用户必须明确确认才能执行。这个审批可以是点击确认,也可以是基于预配置的"一键放行"规则。
这种分级机制不是限制 Agent 的能力,而是将风险与安全控制的粒度对齐。一个只读的天气查询插件和一个能执行 SQL 的数据分析插件,它们的审批流程本来就不应该相同。
零信任模型对 Agent 架构的意义
上述四层防御合起来,本质上是将零信任原则应用到 Agent 的工具调用链上——不信任任何插件、不信任任何返回值、每次调用都验证。
这对 Agent 架构的影响是结构性的。传统的 Agent 设计的隐含假设是:工具是可信的,LLM 是可信的,问题只在于 LLM 是否正确地选择了工具。工具供应链安全架构推翻了这个假设:工具和 LLM 都不可信,信任必须在每次交互中重新建立。
这意味着工具调用循环不再是简单的"LLM 选择工具 → 执行工具 → 返回结果给 LLM"的三步流程。它需要扩展为:
LLM 选择工具并生成参数 策略网关校验参数合法性 沙箱执行工具调用 策略网关校验返回内容 结构化提取返回数据 过滤后的数据送入 LLM 上下文 LLM 处理结果并决定下一步
每一步都引入了检查点和隔离边界。这增加了延迟,但这是 Agent 从原型走向生产时必须接受的代价。
从架构层面评估你的风险
如果你的团队正在使用 MCP 插件,可以问自己几个问题来评估当前的风险水平:
你的 Agent 加载了多少个第三方 MCP 插件?如果超过三个,几乎可以肯定至少有一个插件包含你从未审查过的代码路径。
你的 Agent 是否区分了"来自用户的消息"和"来自工具的数据"?如果两者在 LLM 的上下文中没有边界标记,你的 Agent 已经暴露在工具中毒攻击之下。
你的 MCP 插件是否拥有比它实际需要的更多权限?如果一个天气查询插件能访问文件系统,你的架构存在过度授权的问题。
这些问题的答案决定了你需要在上面提到的四层防御中优先投入哪一层。对于大多数团队来说,输出过滤和策略网关是最容易落地的两个切入点——它们不需要修改 MCP 插件本身,只需要在 Agent 的运行时层添加拦截逻辑。
工具供应链安全不是 MCP 协议的附加功能,也不是某个安全团队的专属职责。当你的 Agent 开始依赖第三方工具时,它就已经进入了这个攻击面。架构师需要做的,不是在事故发生后再检查哪个插件出了问题,而是从一开始就把插件视为不可信的外部实体,在架构层面为每一次工具调用设立检查点。
#AI应用架构 #AI应用开发 #Agent应用架构 #Agent应用开发 #LLM应用架构
夜雨聆风