生产环境中的 MCP 安全
MCP 规范已经强制要求了什么、2026 年编码智能体 CVE 到底击穿了哪一层,以及真正站得住的控制项:受众校验、工具指纹固定、沙箱隔离、出站策略与审计。
- MCP
- 安全
- IAM
- AI 智能体
- OAuth
MCP 服务器不是又一个 API,而是模型会读取并据此行动的指令通道。每一个工具名称、每一段参数描述、每一条工具返回的字符串,都会落进与用户请求同一个上下文窗口,而模型没有可靠办法区分「数据」与「命令」。仅此一点,就让 Model Context Protocol 安全成为不同于 API 安全的独立课题,也解释了 2026 年这些事故为何如此雷同。

本文是运维手册,不是威胁建模论文:规范已经强制要求了什么、2026 年的事故到底击穿了哪一层、以及在生产环境中真正站得住的控制项,每一项都附可执行的验证命令。
MCP 改变了威胁模型中的哪些前提
传统集成里,由开发者决定在代码的哪一处、用什么参数发起哪一次调用。到了 MCP,客户端把所有已连接服务器的工具定义交给模型,由模型决定调用什么、传什么参数。OWASP 把后果说得很直白:MCP 把提示注入、供应链风险与「混淆代理」问题压缩到了同一处。[owaspcs]
- 工具元数据近似可执行内容。描述与 JSON Schema 会被模型当作行动指引读取。被投毒的
description字段更接近注入代码,而不是文档。 - 工具输出是不可信输入。网页内容、issue 标题、数据库行返回上下文后,与系统提示享有同等地位。
- 模型同时看到所有服务器。恶意服务器的工具描述可以改变智能体使用另一台可信服务器的方式,这就是工具影射(tool shadowing),逐台服务器的审查根本发现不了。[owaspcs]
规范明确表示它不会替你解决这些问题:授权对 MCP 实现是可选的;使用 HTTP 传输时应当遵循规范,而 stdio 实现则被要求从环境读取凭据。[spec-auth]其余的沙箱隔离、工具完整性、出站控制,全都留给发布宿主、客户端或服务器的一方。
定下门槛的 2026 年事故
今年最清晰的两个数据点并非来自实验室特设环境,而是厂商自己的默认配置。2026 年 8 月 5 日的 Black Hat USA 上,Novee Security 在三家厂商各自默认交付的配置下,测试了 Anthropic、Google 与 OpenAI 的编码智能体。这项工作产出两个 CVE,均已修复。两者实际允许的操作差别很大,而这正是值得关注的地方。[ghsa-google][ghsa-anthropic]
| 案例 | 真正失效的环节 | 修复版本 |
|---|---|---|
| CVE-2026-12537 — Gemini CLI,CVSS v4 10.0(由 CNA 评分)[cve12537] | 容器启动器中的操作系统命令注入,可通过构造的 .gemini/.env 文件触发。代码在无头 CI 平台的宿主机上执行,且发生在沙箱启动之前。 | Gemini CLI 0.39.1 · run-gemini-cli 0.1.22[ghsa-google] |
| CVE-2026-54316 — Claude Code,CVSS v4 6.0(NVD v3.1 为 9.1)[cve54316] | huggingface.co 被作为裸主机名预先批准给 WebFetch,因此该域名下任意路径(包括攻击者控制的模型仓库)都会被自动放行。HuggingFace 在服务端统计这些请求,于是下载计数器变成了带外隐蔽信道,可外泄智能体能够访问的数据:文件、环境变量、命令输出。仅影响机密性,不涉及代码执行。影响范围为 ≥ 0.2.54 且 < 2.1.163。 | Claude Code 2.1.163[ghsa-anthropic] |
| OpenAI Codex — 无 CVE,无版本更新 | 两次 Codex 运行共用同一份检出,第一次可以写入 AGENTS.md,而第二次会把它当作自己的指令加载。修复发生在工作流层面,而非产品层面。 | 拆分运行;官方指引已把仓库指令文件视为不可信输入[codex] |
这个模式值得精确表述,因为它远不止适用于这三款产品:失效点在「外壳」(harness),不在模型。某个组件把一个值标记为安全,后面的组件却以更高权限对它采取了行动。根据 2026 年 8 月 7 日的公开报道,两个 CVE 均未进入 CISA 的已知被利用漏洞目录,公开记录也未显示任何一条链被用于真实目标——重点是缺陷类别,而不是正在进行的攻击活动。
请把它与DevOps、MLOps 与 LLMOps中的运维模型对照阅读:如果说不出产生某个动作的确切组合——模型、提示、工具 schema、策略——就既解释不了事故,也无法信任回滚。
规范已经强制的基线
「MCP 安全」的工作里,有相当一部分其实只是把 OAuth 做对。2025-06-18 修订把 MCP 服务器重新定义为纯粹的 OAuth 2.1 资源服务器,2025-11-25 修订进一步细化了发现与授权同意。[spec-log]如果你运营远程 MCP 服务器,下面这些不是建议。[oauth21][rfc9700][nsa]
发现机制:靠 RFC 9728,而不是 README
MCP 服务器必须实现 OAuth 2.0 Protected Resource Metadata,并在 authorization_servers 中至少公布一项;客户端必须据此完成发现。面对未认证请求,服务器返回 401,并在 WWW-Authenticate 头中给出元数据文档地址。[spec-auth][rfc9728]
curl -sSI https://mcp.example.com/mcp | grep -i '^www-authenticate'
# www-authenticate: Bearer resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
curl -sS https://mcp.example.com/.well-known/oauth-protected-resource \
| jq '{resource, authorization_servers, scopes_supported}'
curl -sS https://auth.example.com/.well-known/oauth-authorization-server \
| jq '{issuer, token_endpoint, code_challenge_methods_supported}'受众绑定:阻断令牌复用的关键控制
客户端必须在授权请求与令牌请求中都发送 RFC 8707 的 resource 参数,取值为目标服务器的规范 URI,并且无论授权服务器是否支持都要发送。[rfc8707]服务器必须校验所收到的令牌确实是为自己签发的,否则拒绝。[spec-auth]
令牌透传(token passthrough)——接受并非签发给自己的令牌,再原样转发给下游——被明确禁止。规范给出的理由是运维层面的:它摧毁审计链路,绕过依赖受众声明的限流与请求校验,并把你的服务器变成任何持有失窃令牌者的代理。[spec-sec]如果 MCP 服务器要调用上游 API,它在那里是客户端,需要另外获取自己的令牌。
CANONICAL = "https://mcp.example.com/mcp" # RFC 8707 resource identifier
def authorize(token: dict) -> None:
aud = token.get("aud")
aud = aud if isinstance(aud, list) else [aud]
if CANONICAL not in aud:
raise Unauthorized("token audience does not include this server") # 401
if not required_scopes(token).issuperset(scopes_for_current_tool()):
raise Forbidden("insufficient scope") # 403
# Never forward `token` upstream. Mint a separate one for the upstream API.客户端注册:CIMD 成为推荐默认方案
2025-11-25 修订新增了 OAuth Client ID Metadata Documents 作为推荐的客户端注册机制(SEP-991),同时加入 OpenID Connect Discovery 支持,以及通过 WWW-Authenticate 实现的增量 scope 同意。[spec-log][cimd]在 CIMD 中,client_id 就是一个 HTTPS URL,指向描述该客户端的小型 JSON 文档;授权服务器按需拉取、校验已登记的回调地址并套用自身策略——无需事先协调,也不会像动态客户端注册那样堆积一次性注册记录。
这一点很重要,因为 DCR 正是混淆代理攻击的原料之一。当一台充当代理的 MCP 服务器对第三方授权服务器使用静态 client ID,同时允许客户端动态注册,而且第三方还设置了同意 cookie,攻击者就能注册一个带自有 redirect_uri 的客户端,复用受害者的 cookie 跳过同意界面,从而取走授权码。缓解办法是:按客户端存储在服务端的同意记录,在进入第三方流程之前校验,再加上精确匹配的回调地址校验与一次性 state。[spec-sec]
这些都不是全新的身份工程,而是可扩展 IAM 设计的同一套纪律,也与Keycloak 与 Entra ID 的取舍如出一辙。请复用你已经在运营并审计的授权服务器,不要让每一台 MCP 服务器各自长出一套。
十种失效模式与对应的收口控制
OWASP MCP Top 10 目前是 beta(v0.1)阶段的活文档,下一版计划在 2026 年 10 月发布,因此更适合当核对清单,而不是认证目标。[owasp10]
| OWASP MCP 风险 | 能够收口的控制 |
|---|---|
| MCP01 令牌管理不当与密钥泄露 | 使用短生命周期、按服务器与按用户区分的令牌,存放在操作系统凭据库中,绝不写进配置文件。公共客户端启用刷新令牌轮换。[spec-auth] |
| MCP02 权限范围蔓延导致提权 | 从最小 scope 集合起步,通过 WWW-Authenticate scope="…" 挑战逐级提升。不用 *,不用大而全的 scope,也不要把整个目录塞进 scopes_supported。[spec-sec] |
| MCP03 工具投毒(rug pull、schema 投毒、影射) | 在批准时对「名称 + 描述 + 输入 schema」的规范化 JSON 计算 SHA-256 并固定;每次执行前重算,一旦漂移即失败关闭。[owaspcs] |
| MCP04 供应链与依赖篡改 | 固定版本与镜像 digest,校验校验和或签名,扫描依赖,安装前检查仿冒包名。[owaspcs] |
| MCP05 命令注入与执行 | 绝不把模型生成的字符串插入 shell;只用参数数组,配合严格 JSON Schema、additionalProperties: false 与 pattern 约束。 |
| MCP06 意图流劫持 / 上下文提示注入 | 把每一条工具返回都当作数据:剥离形似指令的标记,优先做结构化抽取而非原始 HTML,并对输出中的祈使句式告警。[owaspcs] |
| MCP07 认证与授权不足 | OAuth 2.1 配 PKCE、受众校验、逐请求授权。会话不得用作认证手段。[spec-sec] |
| MCP08 缺少审计与遥测 | 记录每次调用的完整参数、调用方身份、工具哈希与关联 ID;脱敏密钥;接入 SIEM。 |
| MCP09 影子 MCP 服务器 | 建立已批准服务器清单,出站策略拦截其余流量,定期扫描开发机与 CI 镜像。 |
| MCP10 上下文注入与过度共享 | 按任务与租户切分上下文;一次会话检索到的数据不得残留到另一次会话。 |
逐层加固
固定工具定义,否则就等着被 rug pull
rug pull 之所以奏效,是因为用户只批准一次工具,此后再也不看。修法很机械:批准时对定义做哈希,每次调用前校验。
import hashlib, json
def tool_fingerprint(tool: dict) -> str:
canonical = json.dumps(
{"name": tool["name"],
"description": tool.get("description", ""),
"inputSchema": tool.get("inputSchema", {})},
sort_keys=True, separators=(",", ":"), ensure_ascii=False)
return hashlib.sha256(canonical.encode()).hexdigest()
if tool_fingerprint(live_tool) != PINNED[server_id][live_tool["name"]]:
raise ToolDefinitionDrift(server_id, live_tool["name"]) # fail closed要失败关闭。描述自批准以来发生变化的工具就是一个新工具,需要人重新做一次决定,而不是静默重试。
认真隔离本地服务器
本地 MCP 服务器就是一个以客户端权限运行的二进制程序。规范对一键配置的要求毫不含糊:完整显示将要执行的命令、不得截断,明确提示它以客户端权限运行,并在对文件系统与网络的最小访问下做沙箱隔离。[spec-sec]本地优先使用 stdio,因为它把可达范围限制在客户端进程。如果必须用 HTTP,请绑定 127.0.0.1——绝不要 0.0.0.0——要求令牌,并在每次请求时校验 Host 头。[owaspcs]
docker run --rm -i \
--network none \
--read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
--cap-drop ALL --security-opt no-new-privileges \
--pids-limit 128 --memory 512m --cpus 1 \
--user 10001:10001 \
-v "$PWD/workspace:/workspace:ro" \
ghcr.io/example/mcp-server@sha256:<digest>@sha256: digest 是供应链控制项,不是风格偏好:标签可以被重新指向,digest 不能。如果你本来就在 Kubernetes 上跑负载,同样的爆炸半径逻辑也适用,参见什么时候不该用 Kubernetes:每台 MCP 服务器一个容器值得,每台 MCP 服务器一个控制面不值得。
控制出站流量:SSRF 就藏在发现流程里
这一条容易被忽略,因为它打的是客户端而不是服务器。OAuth 发现过程中,客户端会去拉取由服务器提供的 URL:WWW-Authenticate 中的 resource_metadata 地址、authorization_servers 条目,随后是授权服务器元数据里的各个端点。恶意服务器可以把其中任意一项指向 http://169.254.169.254/,借你的客户端读取云实例凭据。[spec-sec]
客户端应当在非回环场景强制 HTTPS,拦截私有与链路本地网段(10/8、172.16/12、192.168/16、127/8、169.254/16、fc00::/7、fe80::/10),对每一跳重定向套用同样规则,并让服务端部署走出站代理。规范明确劝阻自行实现 IP 校验——八进制、十六进制与 IPv4 映射 IPv6 的写法能绕过大多数自制解析器——也提醒防范校验与使用之间的 DNS 重绑定。[spec-sec][owaspssrf]
CI 中的编码智能体:你手上价值最高的攻击目标
CI 里的智能体握有仓库写权限与工作流密钥,而它的输入是陌生人在 issue 里敲下的文字。这正是 Black Hat 那项工作攻击的配置。
on:
issues:
types: [opened]
permissions:
contents: read
issues: read
id-token: none
jobs:
triage:
runs-on: ubuntu-latest
environment: agent-sandbox
timeout-minutes: 10
steps:
- uses: actions/checkout@<commit-sha>
with: { persist-credentials: false }
- run: printf '%s' "$ISSUE_BODY" > /tmp/untrusted.md
env:
ISSUE_BODY: ${{ github.event.issue.body }}
- run: agent-cli review --input /tmp/untrusted.md --sandbox read-only --no-network- 绝不要把
${{ github.event.* }}插值进run:字符串。改用环境变量或文件传递。 - 仓库里的指令文件属于不可信输入。在两次 Codex 运行共用检出、导致第一次可以写入第二次指令之后,OpenAI 自己的指引已经这么写了。[codex]
- 把智能体放在 job 的最后一步。同一份指引提醒,否则它可能给后续特权步骤留下文件。[codex]
- 把多次运行拆进不同 job、使用各自独立的检出,避免前一次写入后一次的输入。
- 给外壳打补丁。Gemini CLI 0.39.1、run-gemini-cli 0.1.22、Claude Code 2.1.163,然后审计所有外部用户可触发的工作流。
审计:让每一次工具调用都可复原
OWASP 把遥测缺失单列为一项风险,而它正是把可控事故变成无边界事故的那一项。[owasp10]每次调用至少要记录:经过认证的调用方身份、服务器与工具、当次校验通过的工具指纹、脱敏后的完整参数、决策路径(自动放行 / 策略允许 / 人工批准)、结果、时延,以及贯穿整次智能体运行的关联 ID。对首次出现的工具、scope 提升,以及工具输出中形似指令的模式设置告警。
有两个结构性决策能让规模超过几台服务器后仍然可控。其一,在前面放一个网关:由单一位置统一实施白名单、按服务器隔离、出站策略与日志记录,胜过在每个宿主里重写同一套策略。其二,能选无聊方案就选无聊方案——无聊的云架构那套论证在这里完全成立:活动部件更少、信任边界更少,混淆代理能藏身的地方也更少。这种克制在搭建智能体技术栈阶段就已经开始回本。
今天就能跑的一轮验证
九项检查:1)未认证请求被拒且给出可用指针;2)受保护资源元数据已发布;3)为其他受众签发的令牌被拒绝;4)不接受明文 HTTP;5)本地服务器没有监听所有网卡;6)工具定义与已批准版本一致;7)镜像按 digest 固定;8)shell 步骤中没有插值事件数据;9)外壳版本不低于修复版本。
# 1
curl -sS -o /dev/null -w '%{http_code}\n' https://mcp.example.com/mcp
# 2
curl -sSf https://mcp.example.com/.well-known/oauth-protected-resource | jq -e '.authorization_servers[0]'
# 3
curl -sS -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $TOKEN_FOR_OTHER_AUDIENCE" \
https://mcp.example.com/mcp
# 4
curl -sS -o /dev/null -w '%{http_code}\n' http://mcp.example.com/mcp
# 5
ss -ltnp | grep -E '0\.0\.0\.0|\[::\]'
# 6
mcp-inspect list-tools --server internal | sha256sum
# 7
grep -rEn 'image:\s*\S+:(latest|main|v?[0-9]+)\s*$' deploy/
# 8
grep -rn 'github\.event\.\(issue\|comment\|pull_request\)' .github/workflows/ | grep 'run:'
# 9
gemini --version; claude --version第 3 项是初次检查时最常失败的一项,也是规范表述最不含糊的一项。
这一周该做什么
MCP 值得采用。这个协议消除了一整类真实存在的集成杂活,2025-11-25 修订也把若干重要的安全决策从口口相传搬进了规范文本。但它交付的是一种能力,不是一套控制面。真正起作用的控制项,都是你在身份治理与供应链工作中已经熟悉的那些,只不过要应用到一条多数团队尚未画出的边界上。
按优先级:给外壳打补丁;把受众校验设为不可选;固定工具定义与镜像 digest;隔离本地服务器并收紧出站;对一切破坏性操作加上人工闸门。未来十二个月里被烧到的组织,不会是「采用了 MCP」的那些,而是「采用了 MCP 却把外壳留在默认配置」的那些。
常见问题
MCP 默认安全吗?
不安全,规范本身也没有这样宣称。授权在 MCP 实现中是可选项,沙箱隔离、工具完整性与网络策略都留给构建宿主、客户端或服务器的一方。本地安装的 MCP 服务器会以启动它的客户端的同等权限运行。
MCP 中的工具投毒是什么?
工具投毒是指攻击者操纵智能体所依赖的工具,从而改变模型行为。OWASP 在这一类下归纳了三种子技术:rug pull,即已批准工具的描述在事后被改动;schema 投毒,即接口定义本身误导模型;以及工具影射,即恶意服务器的描述改变智能体使用另一台可信服务器工具的方式。对名称、描述与输入 schema 固定哈希并在每次调用前校验,可以堵住前两种并发现第三种。
MCP 服务器一定要用 OAuth 吗?
对远程 HTTP 传输而言,实际上是的。规范要求基于 HTTP 的实现遵循其 OAuth 2.1 模型:服务器作为资源服务器,必须实现 RFC 9728 受保护资源元数据,并且必须校验所收令牌确实为自己签发。本地 stdio 服务器改从环境读取凭据,但仍然需要沙箱与同意控制。
什么是令牌透传,为什么被禁止?
令牌透传是指接受一个并非签发给自己的令牌,再原样转发给下游 API。MCP 规范明确禁止:它破坏审计链路,绕过依赖令牌受众的各种控制,并让任何持有失窃令牌的人把你的服务器当成外泄代理。MCP 服务器调用上游 API 时,必须为该 API 单独获取令牌。
目前有 MCP 漏洞正在被野外利用吗?
截至发稿,CVE-2026-12537 与 CVE-2026-54316 均未出现在 CISA 的已知被利用漏洞目录中,公开报道也未显示任何一条链被用于攻击目标。Claude Code 那个问题的公开复现仓库自 2026 年 6 月起就已存在,因此真正的限制因素是你的打补丁速度,而不是利用代码的可得性。
怎样防止恶意 MCP 服务器触达内网?
暴露面在 OAuth 发现路径上:客户端会拉取服务器提供的 URL,恶意服务器可以把它们指向云元数据端点或内部服务。请在非回环场景强制 HTTPS,拦截私有与链路本地网段,对每一跳重定向套用同样校验,并让服务端客户端走出站代理。不要自己手写 IP 校验,编码技巧会绕过大多数自制解析器。
什么是 Client ID Metadata Documents(CIMD)?
CIMD 是 MCP 2025-11-25 修订在 SEP-991 下新增的推荐客户端注册机制。client_id 是一个 HTTPS URL,指向描述客户端及其回调地址的 JSON 文档,由授权服务器按需拉取并校验。它既避免了硬编码 client ID,也避免了动态客户端注册产生的一次性注册堆积,同时移除了混淆代理攻击的一味原料。
参考来源与延伸阅读
本文使用的规范原文、IETF RFC、OWASP 项目材料、美国国家安全局网络安全信息表以及厂商第一手安全公告。
- Model Context Protocol — Authorization (specification 2025-11-25)
- Model Context Protocol — Security Best Practices
- Model Context Protocol — Key Changes, revision 2025-11-25
- SEP-991 — OAuth Client ID Metadata Documents for MCP
- RFC 9728 — OAuth 2.0 Protected Resource Metadata
- RFC 8707 — Resource Indicators for OAuth 2.0
- RFC 9700 — Best Current Practice for OAuth 2.0 Security
- OAuth 2.1 — draft-ietf-oauth-v2-1-13
- OWASP MCP Top 10 (beta, v0.1)
- OWASP Cheat Sheet Series — MCP Security
- OWASP Cheat Sheet Series — SSRF Prevention
- NSA Artificial Intelligence Security Center — CSI: MCP Security Design Considerations for AI-Driven Automation
- GHSA-fg94-h982-f3mm — Out-of-Band Data Exfiltration via Pre-Approved HuggingFace Domain in WebFetch
- NVD — CVE-2026-54316
- GHSA-wpqr-6v78-jr5g — Google run-gemini-cli security advisory
- NVD — CVE-2026-12537
- OpenAI — codex-action security guidance
这篇有帮助吗?