官方 Model Context Protocol(MCP)Python SDK 被曝高危缺陷:恶意 MCP 服务器可诱导客户端把用于登录正规服务的 OAuth 凭证发送到攻击者控制的令牌端点。该问题影响 1.x 与 2.x 两条分支,修复版本为 1.30.0 与 2.2.0,截至 9 月 29 日尚未分配 CVE 编号。
事件概述
安全厂商 Cycode 于 9 月 28 日公开研究,同日 SDK 维护团队发布 GitHub 安全公告。受影响版本为 1.9.1 至 1.29.1 与 2.0.0 至 2.1.1。值得注意的是:修复代码早在 9 月 7 日就随 1.30.0/2.2.0 发布,但被列在 release notes 的 behavior changes 中,并未标注为安全修复,因此不少使用者并不知道需要采取后续动作。
技术细节
MCP 客户端发起登录时,会先向当前连接的服务器询问其授权服务器地址。受影响版本在 OAuth 发现流程的部分路径上未校验授权服务器 issuer,也没有把存储或预置的客户端凭据绑定到对应授权服务器。恶意服务器因此有两种打法:一是直接声明自己的授权服务器;二是不提供受保护资源元数据,再在返回的元数据中把用户的真实授权服务器标注为 issuer,实际指向自己的令牌端点。客户端随后会把 client_secret、授权码与 PKCE code_verifier 一并交出;若使用 PrivateKeyJWTOAuthProvider,泄露的是已签名的客户端断言。PKCE 校验密钥本是一次性防护,泄露后该机制同时失效。
影响评估
官方公告对两类无人值守认证场景按 7.5(高危)计分;交互式 OAuthClientProvider 因需人工触发登录,按 6.5 计分。Cycode 在测试中完整走通了利用链,换取的访问令牌拥有目标应用被授予的全部权限。由于 client_secret 属于长期有效凭据,在轮换前攻击者可反复签发令牌;交互式场景下用户看到的是真实登录页面,难以察觉。受影响前提是:以 MCP 客户端身份通过 HTTP 连接不完全信任的服务器,且持有正规授权服务器的凭据。只构建 MCP 服务端、使用本地 stdio 客户端、或自行附加令牌与请求头的客户端不受影响。目前无在野利用报告。
修复建议
- 升级到 1.30.0(1.x 分支)或 2.2.0(2.x 分支)。
- 仅升级并不够:使用 ClientCredentialsOAuthProvider 或 PrivateKeyJWTOAuthProvider 的,必须显式传入 issuer= 指定凭据归属的授权服务器,否则客户端仍会跟随服务器声明的地址。1.30.0 上该提示仅为 DeprecationWarning,Python 默认不显示,极易被忽略。
- 已弃用的 1.x RFC7523OAuthClientProvider 没有 issuer 选项,应迁移到上述两个 provider。
- 升级后清空一次本地存储的 OAuth 客户端注册信息,使其重新绑定 issuer;自行写入的预注册记录需手工补齐 issuer 字段。
- 若客户端可能连接过不可信服务器,立即在授权服务器轮换 client_secret 并吊销令牌。
pip install -U "mcp>=2.2.0" # 2.x 分支
pip install -U "mcp==1.30.0" # 1.x 分支
# 无人值守 provider 必须显式绑定 issuer
ClientCredentialsOAuthProvider(..., issuer="https://your-auth-server")