콘텐츠로 이동

02-구조

출처는 모두 mcp-security-best-practices. 인용은 원문 + 의역.

위협. MCP 프록시 서버가 third-party API와 OAuth로 연결될 때, 정적 client_id + 동적 클라이언트 등록 + 동의 쿠키가 결합되면 악성 클라이언트가 사용자 동의 없이 인가 코드를 받을 수 있다.

완화 (사양 MUST).

  • Per-Client Consent Storage — 사용자별로 승인된 client_id 레지스트리 유지, 매 전달 전에 검사
  • Consent UI — 요청 클라이언트 이름·요청 스코프·등록된 redirect_uri 표시, CSRF 보호, iframe 차단
  • Consent Cookie__Host- 프리픽스, Secure, HttpOnly, SameSite=Lax, 서명, client_id 바인딩
  • Redirect URI Validation정확 문자열 일치(패턴/와일드카드 금지)
  • OAuth state — 암호학적 난수, 서버 측 저장, 단일 사용

“MCP proxy servers MUST implement per-client consent and proper security controls.”

의역: MCP 프록시 서버는 클라이언트별 동의와 적절한 보안 제어를 반드시 구현해야 한다.

위협. MCP 서버가 자신에게 발급된 토큰인지 검증하지 않고 다운스트림 API로 그대로 흘려보내는 것 — 안티패턴으로 명시.

왜 나쁜가.

  • 레이트 리밋·요청 검증·트래픽 모니터링 우회
  • 감사 추적이 거짓이 됨 (서버가 클라이언트 식별 못 함)
  • 다운스트림 신뢰 경계 깨짐

완화 (사양 MUST NOT).

“MCP servers MUST NOT accept any tokens that were not explicitly issued for the MCP server.”

의역: MCP 서버는 자신을 위해 명시적으로 발급되지 않은 토큰을 절대 받아서는 안 된다.

→ JWT면 aud(audience) 검증, 불투명 토큰이면 introspection 엔드포인트로 검증.

위협. 악성 서버가 메타데이터 필드에 내부 IP, 클라우드 메타데이터(169.254.169.254), localhost, DNS 리바인딩 대상을 박는다.

완화.

  • HTTPS 강제http://는 개발 시 loopback만 허용
  • 사설 IP 차단10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 127.0.0.0/8, ::1, 169.254.0.0/16, fc00::/7, fe80::/10
  • 리다이렉트 검증 — 같은 규칙을 리다이렉트 대상에도 적용
  • Egress 프록시 — Smokescreen 같은 송신 프록시 경유
  • DNS TOCTOU 주의 — 검사 시점과 사용 시점 사이 변경 가능, 결과 핀(pin)

→ 동작하는 가드 코드는 examples/ssrf_guard.py.

위협. 공격자가 세션 ID를 얻어 정상 클라이언트 행세.

완화.

  • 세션을 인증 수단으로 쓰지 말 것 (모든 인입 요청 인증)
  • 세션 ID는 암호학적 난수 UUID (예측 불가)
  • <user_id>:<session_id> 형태로 사용자 바인딩

또한 Streamable HTTP의 Mcp-Session-Id는 가시 ASCII로 제한 — mcp-transports.

위협. 로컬 MCP 바이너리가 악성 startup 명령을 포함하거나, DNS 리바인딩으로 외부에서 로컬 서버에 접근.

완화.

  • 호스트는 1-click 설치 시 명령 전체를 잘라내지 않고 표시, 명시 승인
  • 위험 패턴(sudo, rm -rf) 강조, 민감 위치 경고
  • 가능하면 샌드박스에서 실행
  • 로컬 서버는 stdio 전송 우선, HTTP면 토큰/유닉스 소켓
  • HTTP 서버는 127.0.0.1에만 바인딩 (DNS 리바인딩 차단), Origin 헤더 검증

위협. 와일드카드/omnibus 스코프로 토큰 하나가 모든 권한을 갖게 됨.

완화.

  • 점진적 권한 상승 — 최소 스코프(mcp:tools-basic)로 시작
  • 필요 시 WWW-Authenticate scope="..." 챌린지로 상승
  • 다운스코프(downscope) 허용

흔한 실수.

  • scopes_supported에 모든 스코프 게시
  • *, all, full-access 같은 wildcard
  • 무관한 권한을 묶음
  • 버전 없이 스코프 의미 변경