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과 토큰 verifier가 보고한 리소스를 비교하며, 다르면 HTTP 401로 거부합니다.[1], [2], [7]
프레임워크 매핑
- 이 가이드에서 설명하는 약점의 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에 없는 검사를 이미 구현했을 수 있습니다. | 비교가 정확히 일치하고 의도한 리소스 식별자를 사용하는지 확인하세요. |
소스 코드 비교: 취약한 구현 vs 보안 패치
이 예시는 SDK 문서 형식과 병합된 수정 사항을 따릅니다. verifyJwt는 애플리케이션에 이미 있는 JWT 검증기 또는 introspection 호출을 나타냅니다. 수정 전 middleware에는 expectedResource가 없었고 Audience를 비교하지 않았습니다. 수정 버전은 구성된 resource와 AuthInfo.resource를 비교합니다. Authorization server가 aud에 넣는 정확한 식별자를 사용하세요. 예시는 같은 origin의 Audience URL을 선택한 뒤 전체 resource 비교를 SDK에 맡깁니다. SDK 1.x는 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()이 신뢰하는 authorization server를 통해 토큰을 검증하고 확인된 Audience를AuthInfo.resource로 반환하게 하세요. 발급자가 URL이 아닌 일반 Audience 식별자를 사용한다면 verifier 내부에서 엄격히 확인하고expectedResource는 설정하지 마세요. 값을 추측하거나 URL로 변환하지 마세요.[1], [2], [7] - ✓이 리소스용 토큰은 성공하고 다른 서비스용 토큰, Audience 누락 및 scheme·host·port·path 불일치는 401이 되는지 테스트하세요.[1], [2], [7]
- ✓업데이트만으로 충분하지 않습니다.
expectedResource는 설정 전까지 비활성화되어 있으며, 활성화 후AuthInfo.resource를 반환하지 않으면 모든 요청이 거부됩니다.[1] - ✓예상치 못한 Audience가 있는지 접근 로그와 검증 telemetry를 확인하고, 오용 가능성을 보여 주는 근거가 있을 때만 자격 증명을 폐기하거나 교체하세요.[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