본문으로 건너뛰기

기계 번역입니다. 정식 기준은 영어판이며, 원어민 검수는 아직 완료되지 않았습니다.

MCP

RFC 9728 및 RFC 8707를 사용하여 MCP 서버 관리 — 실제로 작동하는 것

작성자 Olivares AI 8 분 소요

Claude Code를 MCP 서버에 연결합니다. 서버에 인증이 필요합니다. 귀하의 클라이언트가 요청을 보냅니다. 서버는 401 Unauthorizedresource_metadata URL을 전달하는 WWW-Authenticate: Bearer 헤더로 응답합니다. 다음에 일어나는 일은 3개의 RFC와 MCP 관련 확장 세트로 정의된 OAuth 2.1 흐름입니다. 이 흐름은 에이전트 보안의 더 어려운 문제 중 하나를 해결합니다. 즉, MCP 서버와 통신하기 위해 얻은 토큰을 서버에 대해 재생할 수 없도록 하는 것입니다.

이 게시물에서는 사양에 명시된 내용, RFC가 실제로 제공하는 내용, 실제로 보안 함정이 숨어 있는 전체 흐름을 추적합니다.

흐름: 401에서 리소스 바인딩 토큰으로

MCP 인증 모델(2025-11-25 개정판, 2026-07-28로 예정된 최종 사양에 대한 릴리스 후보(동결된 2026-05-21)에 변경 없이 전달됨)는 2단계 프로세스입니다. 1단계는 탐지입니다. WWW-Authenticate: Bearer resource_metadata="..."를 전달하는 401는 클라이언트에게 이 서버가 OAuth로 보호되고 해당 메타데이터를 찾을 수 있는 위치를 알려줍니다. 2단계는 승인된 액세스입니다. 메타데이터를 소비하고, 승인 서버를 검색하고, 이 특정 서버에 바인딩된 토큰을 얻어 사용합니다.

구체적인 단계:

  1. 401 + WWW-인증: MCP 서버는 인증되지 않은 요청을 거부합니다. 챌린지의 resource_metadata 매개변수는 서버의 보호된 리소스 메타데이터 문서를 가리킵니다.

  2. PRM 가져오기(RFC 9728): 클라이언트가 /.well-known/oauth-protected-resource URL을 가져옵니다. 응답은 서버의 정식 리소스 URI, 이를 보호하는 인증 서버, 리소스가 지원하는 범위를 선언하는 JSON 문서입니다. 클라이언트는 문서의 resource 필드가 도달하려는 서버와 일치하는지 확인합니다. 불일치는 가장 신호이므로 거부해야 합니다.

  3. AS 검색(RFC 8414): 클라이언트는 PRM의 authorization_servers 배열에서 발급자 URL을 사용하여 잘 알려진 후보(/.well-known/oauth-authorization-server, 그 다음 /.well-known/openid-configuration)를 탐색하여 인증 서버 메타데이터를 가져옵니다. 반환된 문서의 발급자는 문서를 가져온 발급자와 바이트 동일해야 합니다. 즉, “정규화 후 동일”도 아니고 “충분히 근접”하지도 않아야 합니다. 바이트 동일합니다.

  4. 토큰 획득(RFC 8707): 클라이언트는 인증 및 토큰 요청 모두에서 resource=<canonical server URI>를 포함하여 AS의 토큰 엔드포인트에서 토큰을 요청합니다. 이는 토큰의 대상을 이 특정 MCP 서버에 바인딩합니다. AS는 대상이 해당 리소스인 토큰을 발행하고 클라이언트는 이를 서버에 제공합니다.

  5. 승인된 액세스: 클라이언트는 토큰을 사용하여 MCP 서버의 읽기 전용 자체 검사 메서드(tools/list, resources/list 등)를 호출합니다. 토큰은 클라이언트가 승인되었음을 증명합니다. 리소스 표시기는 토큰이 서버에 대해 발행되었음을 증명합니다.

RFC 9728가 실제로 제공하는 것

RFC 9728(Protected Resource Metadata)는 검색 메커니즘입니다. “이 리소스를 보호하는 인증 서버는 무엇이며, 이는 무엇을 기대합니까?”라고 대답합니다. 서버를 인증하거나, 토큰을 검증하거나, 액세스 제어를 시행하지 않습니다. 그것들은 별개의 관심사입니다.

PRM 문서에는 하나의 필수 필드(resource)와 선택 필드 세트가 있으며, 그중 authorization_servers가 가장 중요합니다. MCP 사양은 RFC의 선택성을 강화합니다. 즉, 하나 이상의 인증 서버가 필요합니다. 아무것도 나열하지 않은 PRM 문서는 오류로 처리됩니다.

resource 필드는 보호되는 리소스의 표준 URI입니다. 클라이언트는 접속하려는 서버와 이를 비교하고 불일치를 거부해야 합니다.

// RFC 9728 §3.3: PRM의 리소스 값은 보호되는 리소스를 식별해야 합니다.
// 클라이언트가 주소를 지정하는 리소스 — 간단한 문자열 비교
// 이 클라이언트가 토큰을 바인딩하는 리소스 표시기입니다.
if prm.Resource != c.resource {
    return authServerMetadata{}, fmt.Errorf(
        "mcp: oauth: protected resource metadata declares resource %q, "+
        "expected %q (RFC 9728 §3.3 reject)", prm.Resource, c.resource)
}

이 검사는 악의적이거나 잘못 구성된 PRM 문서가 다른 리소스를 사용한다고 주장하는 공격 클래스를 방지합니다. 비교는 정규화되지 않습니다. 이는 클라이언트가 제공된 서버 URL에 대해 계산한 정식 리소스 URI에 대한 직접 문자열 일치입니다.

RFC 8707 리소스 표시기 — 토큰 바인딩이 중요한 이유

리소스 표시기가 없으면 AS에서 얻은 액세스 토큰은 잠재적으로 AS가 보호하는 모든 리소스 서버에서 사용될 수 있습니다. 동일한 ID 공급자 뒤에 두 개의 MCP 서버(예: 읽기 전용 문서 서버와 코드 실행 샌드박스)가 있는 경우 둘 중 하나에서 얻은 토큰이 다른 서버에 제공될 수 있습니다. 그것은 혼란스러운 대리인 문제입니다.

RFC 8707는 인증 및 토큰 요청에 resource 매개변수를 추가하여 이 문제를 해결합니다. AS는 대상이 명시적으로 해당 리소스 URI인 토큰을 발행합니다. 적절하게 유효성을 검사하는 리소스 서버는 대상이 자신의 ID와 일치하지 않는 토큰을 거부합니다.

실제로 토큰 요청에는 권한 부여와 함께 리소스 표시기가 포함됩니다.

form := url.Values{
    "grant_type": {"client_credentials"},
    "resource":   {c.resource}, // RFC 8707 — 토큰을 대상에 바인딩
}

이는 커넥터가 사용하는 모든 부여 유형(클라이언트 자격 증명, 인증 코드 상환, 새로 고침 토큰 교체)에 나타납니다. 리소스 표시기는 선택 사항이 아닙니다. 모든 토큰 요청에 존재하므로 모든 토큰은 구성에 따라 대상에 제한됩니다.

바이트가 동일한 발급자 확인

전체 흐름에서 보안이 가장 중요한 유효성 검사는 설명하기가 가장 간단하고 잘못되기 가장 쉽습니다. AS 메타데이터 문서의 발급자 값은 클라이언트가 잘 알려진 URL을 구성하는 데 사용한 발급자와 바이트 동일해야 합니다.

대소문자를 구분하지 않습니다. 체계 정규화 후에는 동일하지 않습니다. 후행 슬래시를 제거한 후에도 동일하지 않습니다. 바이트 동일합니다. RFC 8414 섹션 3.3은 이에 대해 명시하고 있으며 MCP 사양은 요구 사항을 상속합니다.

// discoverASMetadata: issuer가 가져온 대상과 일치하지 않는 문서를
// 거부한다(RFC 8414 §3.3에 따라 issuer는 바이트 단위로 동일해야 한다).
// 일치하지 않는 문서는 가장 신호다.
if as.Issuer != issuer {
    return authServerMetadata{}, fmt.Errorf(
        "mcp: oauth: AS metadata at %s declares issuer %q, "+
        "expected %q (RFC 8414 §3.3 reject)", cand, as.Issuer, issuer)
}

왜 그렇게 엄격합니까? DNS를 제어하거나 네트워크 경로에 있는 공격자가 합법적인 발급자라고 주장하면서 자신의 토큰 엔드포인트를 가리키는 메타데이터 문서를 제공할 수 있기 때문입니다. 클라이언트가 비교하기 전에 발급자를 정규화한 경우 https://auth.example.comhttps://AUTH.example.com가 일치하고 공격자의 문서가 허용됩니다. 바이트 동일 비교로 종료됩니다.

동일한 원칙이 RFC 9207(승인 응답 발급자 검증)에도 적용됩니다. 클라이언트가 인증 코드 흐름을 시작하면 검증된 AS 메타데이터에서 발급자를 기록합니다. 다시 리디렉션할 때 응답의 iss 매개변수는 인증 코드가 사용되기 전에 기록된 값과 일치해야 합니다. 즉, 바이트 단위로 정규화가 필요하지 않습니다. 이것이 바로 혼합 방어입니다. 이 코드가 없으면 악의적인 AS가 코드를 가로채서 클라이언트가 공격자의 토큰 엔드포인트에서 코드를 사용하도록 할 수 있습니다.

클라이언트 식별: CIMD는 DCR를 대체합니다.

MCP 사양은 클라이언트가 인증 서버에 자신을 식별하는 방법에 대한 우선 순위를 정의합니다.

  1. 사전 등록된 자격 증명 - 운영자가 client_idclient_secret를 미리 프로비저닝합니다.
  2. CIMD(클라이언트 ID 메타데이터 문서) — 클라이언트는 HTTPS URL에서 JSON 문서를 호스팅합니다. 해당 URL은 client_id입니다
  3. 동적 클라이언트 등록(RFC 7591) — 클라이언트는 AS의 등록 끝점에 자신을 등록합니다.
  4. 사용자에게 메시지 표시 - 헤드리스 에이전트에는 해당되지 않음

DCR는 CIMD를 위해 2026-07-28로 예정된 최종 사양의 릴리스 후보에서 더 이상 사용되지 않습니다. 그 이유는 운영상의 문제입니다. DCR는 인증 서버에서 영구 클라이언트 상태를 생성합니다. 자체적으로 등록된 모든 에이전트는 AS가 저장해야 하는 client_id/client_secret 쌍을 남기고 누구도 이를 추적하거나 취소하지 않습니다. 에이전트의 경우 이는 통제되지 않은 자격 증명의 확산입니다.

CIMD는 모델을 반전시킵니다. 클라이언트는 자신이 제어하는 ​​URL에서 문서를 호스팅합니다. AS는 클라이언트의 유효성을 검사해야 할 때 문서를 가져와서 HTTP 캐시 헤더에 따라 캐시하고 아무것도 영구적으로 저장하지 않습니다. 키 순환은 문서 업데이트입니다. 클라이언트를 해제하면 문서가 제거됩니다. AS에는 고아 등록이 누적되지 않습니다.

단점: CIMD에서는 클라이언트가 HTTPS 엔드포인트를 실행해야 합니다. 자체 호스팅 거버넌스 플레인의 경우 이는 자연스러운 일입니다. 플레인은 이미 HTTPS 서비스를 실행하고 있습니다. 개발자 노트북에 있는 CLI 도구의 경우 덜 그렇습니다. 따라서 사전 등록된 자격 증명이 우선 순위에서 첫 번째 옵션으로 남아 있습니다.

CIMD ID는 공유 비밀을 전달할 수 없습니다(문서 URL은 공개되어 있습니다. 여기에 포함된 비밀은 자격 증명 유출이 될 수 있습니다). 클라이언트 인증은 private_key_jwt(RFC 7523)를 사용합니다. 클라이언트는 공개 키가 CIMD 문서의 jwks 필드에 게시된 개인 키를 사용하여 단기 JWT에 서명합니다.

”통과하지 않음” 규칙

간과하기 쉬운 구조적 방어: 커넥터는 위에서 설명한 검색 흐름을 통해 특정 서버에 대해 자체적으로 얻은 토큰만 사용합니다. 제3자로부터 토큰을 받아들이지 않고 이를 MCP 서버로 전달합니다. 인바운드 요청에서 토큰을 읽고 전달하지 않습니다.

이는 프로토콜 수준에서 혼란스러운 대리 방어입니다. 클라이언트가 수신한 토큰을 전달하는 경우 공격자는 낮은 권한 리소스로 범위가 지정된 토큰을 제시하고 클라이언트가 이를 높은 권한 리소스로 전달하도록 할 수 있습니다(또는 그 반대로 클라이언트를 속여 공격자가 제어하는 ​​서버에 토큰을 제공하도록 유도하여 높은 권한 토큰을 추출). 자체 토큰을 획득하고 다른 사람의 토큰을 절대 건드리지 않음으로써 커넥터가 토큰 중계 역할을 하도록 만들 수 없습니다.

토큰 패스스루는 권장되지 않을 뿐만 아니라 구조적으로 불가능합니다. OAuth 흐름에 대한 커넥터의 HTTP 클라이언트는 인바운드 요청 처리기와 별개입니다. 들어오는 요청에서 전달자 토큰을 읽고 이를 나가는 요청에 쓰는 코드 경로는 없습니다.

SSRF: DNS 리바인딩 방어

커넥터가 가져오는 모든 메타데이터와 토큰 엔드포인트 URL은 두 계층에서 SSRF로 보호됩니다. 첫 번째는 실행 전 확인입니다. URL은 HTTPS(로컬 개발을 위한 루프백 제외)여야 하며 문자 그대로 예약된 IP 주소가 아니어야 합니다.

두 번째는 DNS 리바인딩 TOCTOU를 닫는 다이얼 타임 확인입니다. 실행 전 확인 중에 공용 IP로 확인되는 호스트 이름은 소켓에 전화를 걸 때까지 개인 IP에 다시 바인딩될 수 있습니다. 커넥터의 HTTP 클라이언트는 연결 시 구체적으로 확인된 IP를 검사하고 예약된 주소를 거부하는 net.Dialer.Control 기능을 설치합니다. 이것이 권위 있는 확인입니다. 비행 전 확인은 명백한 경우에 대한 빠른 거부이지만 다이얼 타임 확인이 중요한 것입니다.

범위 강화

MCP 서버는 WWW-Authenticate: Bearer error="insufficient_scope" scope="mcp:tools:list mcp:resources:read"를 사용하여 요청에 응답할 수 있습니다. 사양(SEP-835/SEP-2350)은 클라이언트가 이를 처리하는 방법을 정의합니다. 즉, 이전에 요청한 범위와 서버가 방금 요청한 범위의 결합을 계산한 다음 확장된 집합으로 토큰을 다시 획득합니다. Union은 이전에 부여된 권한을 유지하고 새 권한을 추가합니다. 스텝업은 한 번 발생합니다. 동일한 요청에 대한 두 번째 범위 부족 문제는 무한 루프를 방지하기 위해 재시도되지 않습니다.

서버는 챌린지에서 상태 비저장이 허용됩니다. 서버는 클라이언트가 이전에 요청했을 수 있는 전체 세트가 아니라 현재 작업에 필요한 범위의 이름만 지정합니다. 클라이언트 측 누적을 통해 서버가 클라이언트별 범위 기록을 추적하지 않고도 이 작업을 수행할 수 있습니다.

이것이 실제로 무엇을 의미하는가

MCP 서버에 대한 OAuth 흐름은 잘 지정되어 있으며 엄격하게 구현되면 서버 간 토큰 재생, 메타데이터 가장, DNS 리바인딩 SSRF 및 제어되지 않는 클라이언트 자격 증명 확산과 같은 실제 공격 표면을 해결합니다. 바이트가 동일한 발급자 확인, 모든 토큰 요청의 리소스 표시기, 구조적 통과 금지 규칙이 핵심 방어입니다. 이는 절반만 구현할 수 있는 기능이 아닙니다. 각 기능은 실패 시 종료되는 엄격한 검사입니다.

더 어려운 문제는 운영이다. MCP 서버를 배포하는 팀은 다음을 수행해야 합니다.

  • 서버의 표준 URI와 일치하는 resource 필드를 사용하여 /.well-known/oauth-protected-resource유효한 PRM 문서를 게시하세요.
  • 리소스 표시기를 지원하는 AS를 사용하세요 — 많은 ID 제공자가 여전히 지원하지 않거나 resource 매개변수를 청중 구속력이 아닌 권고로 취급합니다.
  • 지원 중단이 제거되기 전에 DCR에서 CIMD로 이동하거나 자격 증명을 명시적으로 사전 등록하세요.
  • 발급자에게 자격 증명을 고정하여 AS 토폴로지가 변경될 때 자동 발급자 간 재사용을 방지합니다.

MCP 커넥터 문서는 운영 구성을 다루고, 보안 모델은 리소스 바인딩 토큰이 더 넓은 액세스 맵에 어떻게 적용되는지 설명합니다.

관련 글

자주 묻는 질문

RFC 9728는 내가 대화 중인 MCP 서버가 합법적이라는 것을 보장합니까?

아니요. RFC 9728를 사용하면 클라이언트가 리소스를 보호하는 인증 서버와 필요한 범위를 검색할 수 있지만 이는 리소스에 대한 메타데이터일 뿐 리소스의 신원을 증명하는 것은 아닙니다. TLS 인증서는 서버의 호스트 이름을 증명합니다. PRM는 인증 방법을 알려줍니다. 둘은 상호보완적입니다. TLS 확인이 없는 PRM는 확인되지 않은 호스트에 대한 검색이고, PRM가 없는 TLS는 클라이언트가 토큰을 얻는 방법을 추측하게 합니다.

여전히 작동하는 경우 MCP 사양에서 동적 클라이언트 등록이 더 이상 사용되지 않는 이유는 무엇입니까?

DCR는 인증 서버에서 영구 클라이언트 상태를 생성합니다. 즉, client_id 및 client_secret는 AS가 저장하고 관리해야 합니다. 헤드리스 에이전트의 경우 이는 통제할 수 없는 자격 증명 확장 문제가 됩니다. 모든 에이전트는 스스로 등록하고 아무도 해당 등록을 추적하거나 순환하지 않습니다. CIMD(클라이언트 ID 메타데이터 문서)는 이를 클라이언트가 제어하고 AS가 요청 시 가져오는 호스팅된 문서로 대체합니다. AS에 영구 상태가 없고 고아 등록이 없으며 키 순환은 재등록이 아닌 문서 업데이트입니다.

에이전트가 무엇에 접근할 수 있는지 확인하세요

Olivares AI는 당신의 AI 환경을 위한 개방형 자체 호스팅 플랫폼입니다. 자체 인프라에 배포하고 보안 및 플랫폼 팀이 필요로 하는 액세스 맵을 확보하세요.