flawopen.com/AI Security/mcp-typescript-sdk-bearer-token-audience-validation
MCP TypeScript SDK 未验证 Bearer 令牌的受众
GHSA-rvq5-wwqv-78pq 影响未检查令牌受众便接受令牌的 MCP HTTP 服务器。升级后启用 expectedResource,并将已验证的受众返回为 AuthInfo.resource。[1]
门卫接受了有效门卡,却没有核对它能打开哪栋楼。若同一机构为多栋楼发卡,隔壁楼的门卡也可能通过。MCP服务器必须把门卡的目标与自己的资源URL逐项比对,再决定是否放行。[1]
核心概念与专有名词
框架映射
- 本指南所述弱点的 CWE 参考: CWE-863
攻击执行流程分解
同一授权方为多个 API 发放令牌
受影响部署使用 MCP HTTP Bearer 身份验证,授权服务器会为多个服务签发访问令牌。发给其他 API 的令牌仍可能签名正确且未过期。[1]
受影响的 MCP TypeScript SDK 软件包
| 软件包 | 受影响版本 | 修复版本 |
|---|---|---|
@modelcontextprotocol/sdk | 1.6.0–1.31.0 | 1.32.0 或更高 |
@modelcontextprotocol/server | 2.0.0–2.2.0 | 2.3.0 或更高 |
@modelcontextprotocol/express | 2.0.0–2.0.1 | 2.0.2 或更高 |
@modelcontextprotocol/server-legacy | 2.0.0–2.3.0 | 2.3.1 或更高 |
在 verifyAccessToken() 中自行校验 aud 的任何软件包 | 应用可能已经实现了 helper 中缺失的校验。 | 确认比较是严格匹配,并使用预期的资源标识符。 |
源代码对比:漏洞与安全实现
此示例遵循 SDK 文档格式及已合并的修复。verifyJwt 表示应用已有的 JWT 验证器或 introspection 调用。修复前,middleware 没有 expectedResource,也不会比较受众;修复版会比较配置的资源与 AuthInfo.resource。应使用授权服务器写入 aud 的准确标识;示例选择同一 origin 下的受众 URL,再由 SDK 比较完整资源。将 1.x SDK 升级到 1.32.0 或更高版本;2.x 使用 @modelcontextprotocol/server 2.3.0 或更高版本。使用 Express 时,还需将 @modelcontextprotocol/express 升级到 2.0.2 或更高版本。由于 AuthInfo.resource 的类型是 URL,无法通过 expectedResource 比较不属于 URL 的普通 aud 值:应在 verifyAccessToken() 内严格验证该标识,并在这种部署中不要设置 expectedResource。 verifyJwt 的实现由应用自行提供,此处省略;示例不涉及伪造令牌或利用步骤。[1], [2], [5], [3], [6], [7]
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
const mcpServerUrl = new URL('https://api.example.com/mcp');
const auth = requireBearerAuth({
verifier: {
async verifyAccessToken(token) {
const payload = await verifyJwt(token); // 应用执行 JWT 验证
return {
token,
clientId: payload.sub,
scopes: payload.scopes,
expiresAt: payload.exp,
// 此处未返回 resource,受影响的 helper 也没有 expectedResource 选项。
};
},
},
});
import { requireBearerAuth } from '@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js';
const mcpServerUrl = new URL('https://api.example.com/mcp');
const auth = requireBearerAuth({
verifier: {
async verifyAccessToken(token) {
const payload = await verifyJwt(token); // 应用执行 JWT 验证
// aud 可能是字符串、列表,也可能不存在。返回此服务器对应的条目。
const audience = [payload.aud ?? []].flat().find(
aud => URL.canParse(aud) && new URL(aud).origin === mcpServerUrl.origin,
);
const resource = audience ? new URL(audience) : undefined;
return {
token,
clientId: payload.sub,
scopes: payload.scopes,
expiresAt: payload.exp,
resource,
};
},
},
expectedResource: mcpServerUrl,
});
工程与系统安全加固清单
- ✓盘点使用
requireBearerAuth()或verifyBearerToken()的 MCP HTTP 服务器,并确认授权服务器是否为多个 API 签发令牌。[1] - ✓将 1.x SDK 升级至 1.32.0 或更高版本;2.x 使用
@modelcontextprotocol/server2.3.0 或更高版本,Express 集成还需@modelcontextprotocol/express2.0.2 或更高版本;server-legacy升级至 2.3.1 或更高版本。[1], [3], [6] - ✓在可信服务器配置中,将
expectedResource设为该 MCP 服务器的准确资源标识;不要从入站请求推导该值。[1], [2], [7] - ✓让
verifyAccessToken()通过可信授权服务器验证令牌,并将已验证的受众作为AuthInfo.resource返回。如果发行方使用的普通受众标识不是 URL,应在验证器内严格校验,并且不要设置expectedResource;不要猜测其值或将其强行转换为 URL。[1], [2], [7] - ✓测试本资源令牌可通过;其他服务的令牌、缺少受众以及 scheme、host、port 或 path 不同的受众均返回 401。[1], [2], [7]
- ✓不要认为升级就够了:未配置时
expectedResource仍关闭;启用后若未返回AuthInfo.resource,所有请求都会被拒绝。[1] - ✓检查访问日志和验证遥测中是否出现意外受众;只有证据或暴露评估表明凭据可能被滥用时,才撤销或轮换凭据。[1]
参考来源
- GitHub 安全公告 GHSA-rvq5-wwqv-78pq
- MCP TypeScript SDK PR #2930:为 requireBearerAuth 添加 expectedResource
- MCP TypeScript SDK 1.32.0 发布说明
- MITRE CWE-863:授权不正确
- MCP TypeScript SDK v2 pull request #2929: add expectedResource to bearer-token check
- MCP TypeScript SDK release notes for 2.3.0
- MCP TypeScript SDK server guide: bearer-token audience validation