본문으로 건너뛰기

MCP 서버 보안: OAuth, 대상 바인딩, 그리고 혼동된 대리자

고급
What you'll learn
  • 원격(HTTP) MCP 서버가 단순한 API 키 엔드포인트가 아니라 OAuth 2.1 리소스 서버인 이유를 이해한다
  • 디스커버리 핸드셰이크를 추적한다: 401 → Protected Resource Metadata → Authorization Server Metadata → 토큰
  • 토큰 대상 바인딩(RFC 8707)과 그것이 한 서비스의 토큰이 다른 서비스에서 작동하지 못하게 막는 이유를 설명한다
  • 혼동된 대리자 함정과 그것을 닫는 단 하나의 규칙을 짚는다: 클라이언트의 토큰을 절대 업스트림 API로 그대로 전달하지 말 것
  • MCP 서버를 인터넷에 노출하기 전에 짧은 하드닝 체크리스트를 적용한다

MCP는 신기한 물건에서 에이전트가 도구에 도달하는 기본 방식으로 자리 잡았다 — 이는 곧 MCP 서버가 이제 실제 데이터와 실제 동작 앞에 놓여 있다는 뜻이다. STDIO로 실행하는 로컬 서버는 자신의 환경을 신뢰한다: 환경 변수에서 자격 증명을 읽고, 방어해야 할 네트워크 경계가 없다. 그 동일한 서버를 원격(HTTP)으로 만드는 순간, 그 URL에 도달할 수 있는 누구나 호출을 시도할 수 있다. 그러면 이것은 인가 문제로 바뀌고, MCP 명세는 그 답으로 — 맞춤형 API 키 방식이 아니라 — OAuth 2.1을 제시한다.

이 페이지는 원격 사례에 관한 것이다. 서버가 STDIO 전용이라면, 명세는 명시적으로 OAuth 흐름을 따르지 말라고 한다 — 환경에서 자격 증명을 가져오고 넘어가라.

세 가지 역할

OAuth는 문제를 세 당사자로 나눈다. MCP는 여기에 깔끔하게 대응된다:

MCP OAuth 흐름에서 누가 누구인가
Enter 또는 스페이스 키를 눌러 카드를 뒤집습니다. 좌우 화살표 키로 카드를 이동할 수 있습니다.용어가 표시되었습니다.
1 / 3

핵심적인 사고의 전환: MCP 서버는 로그인 자체를 결코 처리하지 않는다. 그것은 다른 누군가가 발급한 토큰을 검증할 뿐이다. 이 분리 덕분에 당신이 직접 작성한 서버 앞에 기성 아이덴티티 제공자를 둘 수 있다.

디스커버리 핸드셰이크

클라이언트가 어디서 인증할지 미리 구성되어 있어야 할 필요는 없다. MCP는 디스커버리를 자동으로 만들며, 이는 401에 의해 구동된다:

Guided walkthrough1 of 6
  1. 맨 처음 요청은 아무것도 없이 나간다. 서버는 이를 HTTP 401 Unauthorized와 자신의 리소스 메타데이터 URL을 가리키는 WWW-Authenticate 헤더로 거부한다.

클라이언트 측에 하드코딩된 인증 구성이 없다는 점에 주목하라 — 401이 모든 것을 부트스트랩한다. 바로 이것이 핵심이다: 에이전트는 한 번도 본 적 없는 서버에 연결하여 어떻게 인증할지 알아낼 수 있다.

대상 바인딩: 하중을 견디는 규칙

여기 대상 바인딩이 막으려고 존재하는 실패 모드가 있다. 사용자가 calendar.example.com용으로 발급된 토큰을 가지고 있다고 하자. evil.example.com에 있는 악의적인(혹은 그저 부주의한) MCP 서버가 클라이언트를 속여 토큰을 자신에게 보내게 만든다. 만약 evil이 그것을 받아들이면, 이제 사용자로 가장해 캘린더 API를 호출할 수 있다. 한 서비스의 토큰이 다른 서비스에서 작동한 것이다. OAuth의 보안 경계가 방금 붕괴했다.

해결책은 **Resource Indicators (RFC 8707)**이다:

Guided walkthrough1 of 3
  1. 인가 요청과 토큰 요청 둘 다에서, 클라이언트는 호출하려는 MCP 서버의 정규 URI로 설정된 resource 파라미터를 반드시 포함해야 한다 — 예: resource=https://mcp.example.com. AS가 이를 지원하는지 확실하지 않더라도 이를 보낸다.

인가 요청의 resource 파라미터 (URL-encoded)

&resource=https%3A%2F%2Fmcp.example.com

정규 URI는 엄격하다: https://mcp.example.comhttps://mcp.example.com:8443/mcp는 유효하고, mcp.example.com(스킴 없음)과 https://mcp.example.com#frag(프래그먼트)는 유효하지 않다. 상호운용성을 위해 끝에 슬래시가 없는 형태를 선호하라.

혼동된 대리자: 토큰을 절대 그대로 전달하지 말 것

이것은 선의의 MCP 서버를 공격자의 프록시로 바꿔 놓는 실수다. 에이전트 보안의 그 혼동된 대리자 문제와 동일하되, 하나의 구체적인 규칙으로 날카롭게 벼려낸 것이다.

MCP 서버는 종종 업스트림 API(GitHub, 데이터베이스 서비스, 다른 SaaS)를 호출해야 한다. 유혹은 클라이언트가 당신에게 건넨 토큰을 받아 업스트림으로 전달하는 것이다. 그러지 마라. 명세는 단호하다: MCP 서버는 클라이언트로부터 받은 토큰을 반드시 그대로 전달해서는 안 된다.

왜 위험한가: 클라이언트의 토큰은 당신의 서버를 대상으로 하여 발급되었다. 만약 당신이 이를 전달하면, 업스트림 API는 그것이 당신에게서 온 것처럼 신뢰하거나 당신이 이미 검증했다고 가정할 수 있다 — 그리고 이제 한 홉으로 스코프가 정해진 토큰이 누구의 동의 모델에도 없는 곳에서 두 홉 떨어진 곳까지 일을 하고 있다.

Watch out
  • 당신의 MCP 서버가 업스트림 API를 호출한다면, 그것은 그 API에 대해 별개의 OAuth 클라이언트로 동작하며 업스트림 authorization server로부터 자신만의 토큰을 얻는다. 독립적인 두 개의 토큰, 독립적인 두 개의 대상. 클라이언트의 토큰은 당신의 문 앞에서 멈춘다.

사전 점검 하드닝 체크리스트

원격 MCP 서버가 공개 인터넷에 닿기 전에:

Guided walkthrough1 of 7
  1. 모든 AS 엔드포인트는 반드시 HTTPS여야 한다. 리디렉션 URI는 반드시 HTTPS 또는 localhost여야 한다 — 그 외에는 안 된다.

스스로 점검하기

스스로 점검하기

0/4
  1. 원격 MCP 서버가 액세스 토큰 없는 요청을 받는다. 명세는 서버가 가장 먼저 무엇을 하도록 요구하는가?
  2. 토큰 대상 바인딩(RFC 8707)은 무엇을 방어하는가?
  3. 당신의 MCP 서버가 업스트림 GitHub API를 호출해야 한다. 클라이언트가 보낸 액세스 토큰으로 무엇을 해야 하는가?
  4. STDIO(로컬) MCP 서버의 경우, 명세는 자격 증명을 어떻게 처리해야 한다고 말하는가?

출처 및 더 읽을거리