flawopen.com/AI Security/mcp-typescript-sdk-bearer-token-audience-validation
MCP TypeScript SDKでBearerトークンのAudience検証が不足
GHSA-rvq5-wwqv-78pqは、Audienceを確認せずトークンを受け入れるMCP HTTPサーバーに影響します。更新後、expectedResourceを有効にし、検証済みAudienceをAuthInfo.resourceで返してください。[1]
警備員が有効な入館証を受け取っても、どの建物に入れる証か確認しない状態です。同じ発行元が複数の建物向けに証を発行すると、隣の建物向けの証でも通れることがあります。MCPサーバーは証の宛先を自分のリソースURLと照合する必要があります。[1]
主要な概念と専門用語
Audience(`aud`)- アクセストークンの発行対象となるリソースまたはサービスです。署名が有効でintrospectionが成功しても、そのトークンがこのMCPサーバー向けとは限りません。[1], [7]
`expectedResource`- 修正版middlewareで明示的に有効化する設定です。設定済みのリソースURLとトークン検証器が報告するリソースを比較し、不一致ならHTTP 401で拒否します。[1], [2], [7]
フレームワーク対応
- このガイドで説明する弱点の CWE 参照: CWE-863
ステップ・バイ・ステップの攻撃フロー
1つの発行元が複数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に欠けている検証を実装済みの場合があります。 | 比較が厳密で、意図したリソース識別子を使っていることを確認してください。 |
ソースコード比較:脆弱 vs 堅牢化
この例はSDKのドキュメント形式と取り込まれた修正に沿っています。verifyJwtはアプリに既存のJWT検証またはintrospection処理を表します。修正前のmiddlewareにはexpectedResourceもAudience比較もありませんでした。修正版は設定リソースとAuthInfo.resourceを比較します。認可サーバーがaudに設定する正確な識別子を使い、例では同じoriginのAudience URLを選んでSDKに完全なリソース比較を任せます。1.xはSDK 1.32.0以降へ、2.xは@modelcontextprotocol/server 2.3.0以降へ更新してください。Expressでは@modelcontextprotocol/expressも2.0.2以降が必要です。AuthInfo.resourceはURL型のため、URLではない単純なaud値をexpectedResourceで比較することはできません。その場合は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] - ✓SDK 1.xは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()で信頼する認可サーバーを使ってトークンを検証し、検証済みAudienceをAuthInfo.resourceとして返します。発行元がURLではない単純なAudience識別子を使う場合は、検証処理内で厳密に照合し、expectedResourceは設定しないでください。値を推測したりURLに変換したりしないでください。[1], [2], [7] - ✓このリソース向けトークンは成功し、別サービス向けトークン、Audienceなし、scheme・host・port・pathが異なるAudienceは401になることをテストします。[1], [2], [7]
- ✓更新だけでは不十分です。
expectedResourceは設定しない限り無効であり、有効化後にAuthInfo.resourceがなければすべての要求が拒否されます。[1] - ✓予期しないAudienceがないかアクセスログと検証テレメトリーを確認します。資格情報の悪用を示す証拠がある場合に限り、失効またはローテーションします。[1]
参考資料
- GitHub Security Advisory GHSA-rvq5-wwqv-78pq
- MCP TypeScript SDK pull request #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