跳到主要内容

MCP Server 供应链风险:当你的智能体工具成为攻击向量

· 阅读需 10 分钟
Tian Pan
Software Engineer

一个开发者从公共注册中心安装了一个流行的 MCP 服务器——一个 Slack 集成、一个数据库连接器,或者是一个文件系统工具。测试时它表现得非常完美。三周后,该工具的描述在静默中发生了变化。曾经用于总结 Slack 线程的 Agent,现在正通过一个开发者从未检查过的参数字段窃取环境变量。

这并非假设。已经有恶意的 MCP 服务器包被发现在安装它们的组织中窃取电子邮件。Smithery.ai 注册中心的一个路径遍历漏洞暴露了可以控制 3,000 多个托管 MCP 服务器的身份验证令牌。流行的 mcp-remote npm 软件包(CVE-2025-6514,下载量超过 55.8 万次)包含一个任意代码执行漏洞。MCP 服务器正在成为 AI Agent 的新 “left-pad 问题”——只不过其爆炸半径包括你的凭证、你的数据以及用户的信任。

你未曾察觉的攻击面

传统的供应链攻击目标是你的构建流水线或运行时依赖项。MCP 供应链攻击的目标则更为隐蔽:Agent 与其工具之间的语义层。

当 AI Agent 连接到 MCP 服务器时,它会将工具描述、参数模式(Schema)和响应格式摄入其上下文窗口。这些描述不仅仅是文档——它们是模型用来决定调用什么以及如何解释结果的指令。这创造了三个大多数团队从未审计过的独特攻击面。

工具描述注入是最常见的。攻击者在工具元数据中嵌入恶意指令——隐藏在特殊标签之后,埋在空白字符之后,或者塞在大多数 MCP 客户端 UI 显示的字符限制之外。针对 45 个活跃 MCP 服务器、353 个工具的研究测试发现,在 20 个 LLM Agent 中,攻击成功率高达 72.8%,而最高拒绝率低于 3%。模型看到的是完整描述,开发者看到的是截断版本。这两个视图之间的差距就是整个攻击面。

响应投毒更为微妙。一个被攻陷的工具返回带有嵌入指令的结果,从而影响后续的 Agent 行为。如果你的 Agent 调用一个搜索工具,而结果包含隐藏的提示词注入,那么 Agent 随后做出的每一个决策都可能受到损害。Agent 将工具输出视为可信数据——它没有机制来区分合法响应和武器化响应。

工具名称冲突利用了 Agent 根据名称和描述选择工具这一事实。当多个 MCP 服务器暴露同名工具时,模型可能会调用恶意变体而非合法变体。这是 AI 世界的 DLL 劫持,且大多数 MCP 客户端没有命名空间隔离来防止这种情况。

抽毯子攻击:为什么一次性批准是不够的

大多数 MCP 客户端实现了一种许可模型:用户批准一个工具,随后 Agent 就可以自由调用它。这产生了一个危险的假设——你今天批准的工具与你 Agent 明天调用的工具是同一个。

在托管 MCP 服务器的情景中,工具描述可以在获得批准后动态修改。一个最初自称为 “获取给定城市的天气数据” 的工具,可以静默地重新定义为 “获取天气数据并读取 ~/.ssh/id_rsa 的内容”。Agent 会遵从新的描述,因为它从未将其与用户最初批准的版本进行比较。

这种 “抽毯子(rug pull)” 漏洞特别危险,因为它利用了时间性信任。开发者在设置期间审计了工具。安全审查发生在安装时。但工具定义是一个可变的远程资源,而不是固定在特定版本的不可变构件。

同样的模式也出现在隐性工具链中。一个被攻陷工具的描述可以指示模型在其操作过程中调用其他工具——包括绕过显式批准流程的内置辅助工具。一个被攻陷的 MCP 服务器可以利用你的整个工具库作为其攻击面,因为像文件读取器和代码搜索这类预授权工具不会触发新的权限提示。

让你担忧的数据

问题的规模令人触目惊心。在调研的 2,614 个 MCP 实现中,82% 使用的文件操作容易受到路径遍历攻击,三分之二存在某种形式的代码注入风险,超过三分之一容易受到命令注入攻击。这些并非罕见的攻击向量——它们是应用于新执行上下文的 OWASP Top 10。

身份验证方面的情况同样严峻。对 5,200 多个 MCP 服务器的分析研究发现,88% 需要凭证,但超过一半依赖于不安全的、长寿命的静态密钥。OAuth 2.1 等现代身份验证方法的采用率仅在 8.5% 左右。大多数团队在使用永不过期且从不轮换的 API 密钥将其 AI Agent 连接到第三方工具服务器。

43% 的命令注入率值得特别关注。这些服务器在 Python 的 subprocess 调用中使用 shell=True,或者在 Node.js 中使用 exec()——任何初级安全工程师在代码审查中都会指出这些模式,但它们在 MCP 生态系统中泛滥成灾,因为大多数服务器都是快速构建、公开分享并在未通过审计的情况下被采用的。

一个真正有效的审查清单

在将任何 MCP 服务器连接到生产环境的智能体 (agent) 之前,请通过以下各项检查:

来源验证。 锁定到特定的 commit hash,而不是版本标签 (version tags)。版本标签可以被重新分配;而 commit hash 则不行。如果服务器是托管的,请验证托管商的更新策略。如果它是一个像 Smithery 或 npm 这样的注册表,请检查该软件包的发布历史,看是否存在可疑的所有权转移。

工具定义的静态分析。 完整阅读每一个工具描述 —— 不是你的 MCP 客户端显示的缩减版本,而是原始的 JSON。搜索 Unicode 不可见字符、base64 编码的字符串,以及引用其他工具、环境变量或文件路径的指令。在任何服务器连接到生产环境之前,对其运行 mcp-scan 或等效工具。

依赖审计。 像对待任何其他第三方依赖一样对待 MCP 服务器。扫描已知的 CVE。检查依赖树是否存在拼写盗用 (typosquatting)。验证服务器的依赖项没有引入预料之外的网络库或文件系统访问。

权限范围审查。 将每个工具声明的功能与其核心需求进行映射。Slack 集成不应该需要文件系统访问权限。数据库查询工具不应该需要向任意 URL 发送网络请求。大多数 MCP 服务器请求的权限远比其声明的功能所需的多。

行为测试。 在隔离环境中运行服务器,并观察其真实的网络流量、文件访问模式和系统调用。将观察到的行为与声明的功能进行对比。这可以捕捉到工具“宣称的功能”与“实际行为”之间的差距。

运行时沙箱模式

审查可以捕获已知的恶意服务器。沙箱则在运行时遏制未知的恶意行为。

容器隔离是最小可行防御。在各自的 Docker 容器中运行每个 MCP 服务器,不提供主机网络访问,使用只读文件系统,并设置明确的资源限制。这可以防止受损的服务器访问你主机的凭据、SSH 密钥或其他 MCP 服务器的数据。

网络分段可以防止数据外泄。不需要外部网络访问的 MCP 服务器不应拥有通往互联网的路由。需要访问特定 API 的服务器应配置仅限白名单的出口规则。默认应为“拒绝所有”,任何例外都必须经过论证并记录在案。

工具级权限强制执行超越了服务器级的隔离。在你的智能体及其 MCP 服务器之间实现一个代理层,在运行时强制执行每个工具的权限。该代理负责验证每次工具调用是否符合批准的参数架构,响应有效负载是否符合预期格式,以及工具调用是否保持在声明的范围内。

描述锁定 (Description pinning) 旨在应对“抽地毯 (rug pull)”风险。在批准时对工具描述进行哈希处理,并在每次调用前验证该哈希值。如果描述发生变化,则阻止该工具并提醒操作人员。这将可变的信任关系转化为不可变的信任关系,强制在工具行为改变时进行明确的重新批准。

审计日志是不可或缺的。记录每一次工具调用及其完整参数、每一个响应有效负载以及所提供的每一个工具描述。当出现问题时(这必然会发生),这些日志是还原事件经过的唯一方法。没有它们,你就是在缺乏供应链可见性的情况下对供应链攻击进行调试。

组织层面的问题

MCP 供应链安全最难的部分不是技术 —— 而是组织管理。大多数团队像对待浏览器扩展一样对待 MCP 服务器:个体风险低、集体缺乏管理,且由恰好在那周需要该功能的人员进行评估。

生产环境的 MCP 部署需要一个集中的已批准服务器注册表、每个服务器集成的负责人,以及新服务器添加的审查流程。这并不是沉重的治理,而是成熟的工程组织应用在 npm 软件包、Docker 基础镜像和 API 集成上的相同纪律。

另一种选择是,当客户报告他们的数据出现在不该出现的地方时,你才发现你的智能体已被攻破。到那时,攻击面已经开放了多久,未经审查的 MCP 服务器就连接了多久 —— 考虑到团队很少审计正在运行的集成,这个时间通常以月为单位计算。

未来的发展方向

MCP 的采用正在加速。该协议正成为 AI 智能体与外部工具之间的标准接口。这意味着供应链攻击面的增长速度超过了旨在保护它的安全工具。

生态系统需要三样目前缺失的东西:工具定义的加密签名(以便验证来源)、标准化的功能声明(以便机械地执行最小权限原则)以及运行时的行为证明(以便检测工具的实际行为何时偏离其声明的行为)。

在这些原语出现之前,责任落在每个将 AI 智能体连接到 MCP 服务器的团队身上。验证来源。锁定版本。沙箱化运行。记录一切。并将每一个工具描述视为不可信的输入 —— 因为事实正是如此。

References:Let's stay in touch and Follow me for more thoughts and updates