flawopen.com/AI Security/mcp-python-sdk-oauth-issuer-confusion
MCP Python SDK OAuth 발급자 검증 결함
GHSA-qx49-fqc8-xw99는 HTTP MCP 서버가 영향받는 Python SDK OAuth 클라이언트의 자격 증명 전송 대상을 바꿀 수 있게 했습니다. mcp 1.30.0 또는 2.2.0으로 업그레이드하고 머신 간 제공자에는 issuer=를 설정하세요. CVE는 부여되지 않았습니다.[1], [3], [8]
회사가 봉인된 열쇠를 승인된 안내 카드에 적힌 은행 주소로 보낸다고 가정해 보세요. MCP 서버가 그 카드를 제공할 수 있었고, 한 대체 경로는 카드의 항목이 예상한 은행의 것인지 확인하지 않았습니다. 발급자 검증은 열쇠를 보내기 전에 카드의 신원을 설정에서 신뢰하는 은행과 비교합니다.[1], [2]
핵심 개념 및 용어
MCP 클라이언트- Model Context Protocol로 MCP 서버가 제공하는 도구나 데이터에 연결하는 애플리케이션입니다. 이 문제는 OAuth 제공자와 함께 Python SDK를 HTTP 클라이언트로 사용할 때 영향을 줍니다.[1]
Authorization server 발급자- 토큰을 발급하는 OAuth 서비스의 안정적인 식별자입니다. 클라이언트는 검색된 메타데이터를 독립적으로 예상한 발급자와 비교해야 합니다.[1], [2]
Protected Resource Metadata (PRM)- 보호된 리소스의 authorization server를 식별할 수 있는 메타데이터입니다. PRM이 없을 때 SDK는 레거시 검색 경로도 사용합니다.[1], [2]
프레임워크 매핑
단계별 공격 실행 흐름
HTTP MCP 클라이언트가 OAuth 자격 증명을 보유
애플리케이션은 영향받는 mcp 버전과 SDK OAuth 제공자 중 하나를 사용합니다. 클라이언트 시크릿, 서명 키 또는 저장된 클라이언트 등록 정보를 가진 채 완전히 신뢰할 수 없는 MCP 서버에 연결할 수 있습니다.[1]
서버가 인증 서버 검색에 영향을 줌
mcp 1.9.1–1.29.1은 모든 검색 경로에서 발급자를 검증하고 자격 증명을 바인딩하지 못했습니다. mcp 2.0.0–2.1.1은 PRM이 없어 레거시 fallback이 실행되거나 403 insufficient_scope 후 추가 검색을 수행할 때 영향받았습니다. 두 버전 계열의 머신 간 제공자 모두 사전 제공 자격 증명을 바인딩하는 issuer=가 없었습니다. advisory의 패키지 범위는 프리릴리스 2.0.0a1부터 <2.2.0까지이므로 영향받는 2.0.0 프리릴리스도 포함됩니다.[1], [3], [8]
영향은 제공자와 권한에 따라 달라짐
수신자는 노출된 자격 증명이나 토큰을 부여된 권한 범위에서 사용할 수 있습니다. 권고는 무인 제공자를 CVSS 7.5, 사람이 로그인을 시작해야 하는 대화형 제공자를 6.5로 평가합니다. 권고에 따르면 CVE는 아직 할당되지 않았습니다.[1]
MCP Python SDK OAuth 버전 및 대응 매트릭스
| 클라이언트 구성 | 문서에 명시된 영향 조건 | 필요한 조치 |
|---|---|---|
mcp 1.9.1–1.29.1을 사용하는 HTTP OAuth 클라이언트 | 모든 검색 경로에서 발급자 검증과 자격 증명의 발급자 바인딩이 일관되게 이뤄지지 않았습니다. | mcp 1.30.0 이상으로 업그레이드하고 실제 배포 버전을 확인하세요. |
mcp 2.0.0–2.1.1을 사용하는 HTTP OAuth 클라이언트 | PRM이 없어 레거시 fallback을 사용하는 경우 또는 403 insufficient_scope 후 step-up 검색에서 영향받습니다. advisory의 패키지 범위는 프리릴리스 2.0.0a1부터 <2.2.0까지이므로 영향받는 2.0.0 프리릴리스도 포함됩니다. | mcp 2.2.0 이상으로 업그레이드하고 실제 배포 버전을 확인하세요. |
사전 발급 자격 증명을 사용하는 ClientCredentialsOAuthProvider 또는 PrivateKeyJWTOAuthProvider | 영향받는 버전에는 자격 증명을 예상 authorization server에 바인딩하는 issuer= 설정이 없었습니다. | 업그레이드 후 issuer=를 명시적으로 설정하세요. 1.x의 deprecated RFC7523OAuthClientProvider에서 마이그레이션하세요. |
| SDK로 만든 서버, stdio 클라이언트 또는 자체 토큰/헤더를 제공하는 클라이언트 | 이 advisory의 영향 대상이 아닙니다. | 이 결함에 대해서는 조치가 필요 없습니다. 영향받는 HTTP OAuth 클라이언트도 사용한다면 다시 평가하세요. |
근본 원인 분석 (Root Cause)
이 신뢰 경계 결함은 서로 연결된 두 부분에서 발생했습니다. 영향을 받는 일부 검색 경로가 authorization-server 메타데이터의 issuer를 MCP 서버와 독립적으로 예상한 값과 일관되게 비교하지 않았고, 저장되었거나 사전 제공된 자격 증명도 원래 발급자에 묶여 있지 않았습니다. 따라서 악의적이거나 침해된 MCP 서버가 토큰 교환을 자신이 통제하는 엔드포인트로 유도해 클라이언트의 OAuth 정보를 받을 수 있었습니다. 수정 사항은 검색 전에 예상 발급자를 정하고 불일치를 거부하며 저장된 등록 정보를 해당 발급자에 바인딩합니다. 비대화형 machine-to-machine 공급자에는 명시적인 issuer=도 필요합니다.[1], [2]
소스 코드 비교: 취약한 구현 vs 보안 패치
수정된 SDK 버전에서 issuer 인자를 사용할 수 있습니다. 머신 간 자격 증명을 해당 authorization server에 연결하지만, 업그레이드나 기존 미연결 등록 정보 삭제, 노출 가능성이 있는 자격 증명 교체를 대신하지는 않습니다.[2], [3], [8]
소스 코드 발췌: mcp.client.auth
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
)
import os
from mcp.client.auth import ClientCredentialsOAuthProvider
auth = ClientCredentialsOAuthProvider(
client_id=os.environ["MCP_CLIENT_ID"],
client_secret=os.environ["MCP_CLIENT_SECRET"],
issuer=os.environ["MCP_AUTH_ISSUER"],
)
# mcp >= 1.30.0 (1.x) 또는 >= 2.2.0 (2.x)를 사용하세요.
엔지니어링 및 시스템 보안 강화 체크리스트
- ✓HTTP MCP 클라이언트를 조사하고 전이 의존성과 컨테이너 이미지까지 포함해 설치된
mcp버전을 확인하세요.[1] - ✓1.x 클라이언트는
mcp1.30.0 이상으로, 2.x는 2.2.0 이상으로 업그레이드하고 잠금 파일과 배포 환경을 확인하세요.[1], [3], [8] - ✓
ClientCredentialsOAuthProvider와PrivateKeyJWTOAuthProvider에issuer=를 명시하세요. 해당 옵션이 없는 사용 중단 예정 1.xRFC7523OAuthClientProvider에서 마이그레이션하세요.[1], [2] - ✓발급자 연결 기능이 추가되기 전에 저장한 OAuth 등록 정보를 삭제하고 다시 등록하세요. 직접 사전 등록한 정보에는 발급자를 명시하세요.[1], [2]
- ✓FlawOpen 구현 권장 사항: 신뢰하지 않는 MCP 서버를 자격 증명이 전달될 수 있는 신뢰 경계로 다루세요. 정책상 허용되지 않는 한 프로덕션 OAuth 시크릿이나 서명 키를 가진 클라이언트를 연결하지 마세요.[1]
- ✓영향받는 클라이언트가 신뢰하지 않는 서버에 연결했을 가능성이 있으면 클라이언트 시크릿을 교체하고 authorization server에서 토큰을 폐기하세요.[1]
- ✓1.x 및 2.x 검색 경로, 발급자 불일치, PRM 누락,
403 insufficient_scope, 발급자에 바인딩된 등록 정보,issuer=가 없는 머신 간 제공자에 대한 회귀 테스트를 추가하세요.[2] - ✓다른 클라이언트를 영향받는 것으로 분류하기 전에 advisory 범위를 확인하세요. SDK로 만든 서버, stdio 클라이언트, 자체 토큰이나 헤더를 제공하는 클라이언트는 영향받지 않습니다.[1]
출처
- GitHub-reviewed advisory GHSA-qx49-fqc8-xw99
- MCP Python SDK pull request #3398: Validate issuer on every discovery path
- MCP Python SDK v1.30.0 release notes
- MITRE CWE-345: Insufficient Verification of Data Authenticity
- The Hacker News: Official MCP Python SDK flaw can let malicious servers steal OAuth credentials
- Cycode disclosure: MCP Python SDK OAuth flaw
- CWE-522: Insufficiently Protected Credentials
- MCP Python SDK v2.2.0 release