CCAR-P 개념 카드

■ MCP 인증·인가

MCP authentication/authorization3.2 본편

표시가 없는 문장은 공식 문서에서 확인한 내용이에요. 다른 표시는 읽는 법

왜 배우나 — 시험이 요구하는 것

시험 목표 3.2 인증·권한 빈틈 · D3 연결 19%
Analyze authentication and authorization requirements to identify security gaps

이 개념으로 푸는 판단 (할 수 있어야 하는 것)

  • 3.2① 같은 머신의 stdio MCP 서버와 원격 HTTP MCP 서버를 함께 붙인다 — 자격증명을 각각 어떻게 주나? — 전송 방식별 인증 고르기
  • 3.2② 사내 MCP 서버가 받은 사용자 토큰을 Salesforce API 호출에 그대로 붙인다 — 무엇이 빈틈인가? — 토큰 경계 (audience 검증 · 그대로 넘기기 금지)
  • 7.1③ 에이전트에 줄 권한, 어디까지? — 행동을 allow/ask/deny 로 나눈 공유 설정 + MCP 자격증명 최소 권한

한눈에

MCP 에서 인가(authorization)는 **선택(OPTIONAL)** 이다. 붙인다면 전송 방식이 방법을 정한다. stdio 는 환경에서 자격증명을 받는다. HTTP 는 OAuth 2.1 기반 규격을 따른다

HTTP 인가에서 역할은 셋이다

  • MCP 서버 : OAuth resource server
  • MCP 클라이언트 : OAuth client
  • 토큰 발급 : authorization server

비유 같은 파드 사이드카는 마운트된 시크릿·환경변수를 쓴다. 외부 API 는 OIDC/OAuth 토큰을 쓴다. 게이트웨이가 받은 JWT 를 제3자 API 에 재사용하지 않는 것과 같다

MCP 인가는 선택이고, 붙인다면 stdio 는 환경에서 자격증명, HTTP 는 OAuth 2.1 기반 규격(서버 = resource server, 클라이언트 = OAuth client)을 따른다는 비교 그림.
그림으로 보기 (AI 생성 · Codex · 누르면 크게)

ELI5

놀이공원 손목 띠는 그 놀이공원에서만 써요.

  • 집 안 방끼리는 띠 없이 열쇠를 서랍에 두고 써요(로컬).
  • 밖에 나갈 땐 입구에서 띠를 받아요(원격).
  • 옆 놀이공원 입구에 내 띠를 내밀면 안 돼요 — 거기서 새 띠를 받아요.
  • 처음엔 작은 띠만 받고, 큰 놀이기구 앞에서 더 받아요.

개념 설명

영상 — Salesforce 엔 어떤 토큰을 낼까? (약 43초 · 소리 없음 · 자막). 호출 3번·기록부 칸·한도 횟수·'주워 온 토큰'은 그림용 가정 · 화면의 '대상:' 은 토큰의 aud 칸 작은 화면에서는 전체화면(⛶)으로 보세요.

원리

인가(authorization)는 선택이다. 붙이면 전송이 방법을 정한다

  • stdio 서버는 클라이언트가 띄운 같은 머신의 자식 프로세스다. 그래서 네트워크 경계가 없다. 자격증명은 환경(환경 변수)으로 주면 된다. 규격에는 OAuth 규격을 따르지 말라고(SHOULD NOT) 적혀 있다.
  • HTTP 서버는 네트워크 너머에 있다. 그래서 OAuth 2.1 을 쓴다. 역할은 셋이다.
    • MCP 서버 : resource server(토큰을 검사한다)
    • 클라이언트 : OAuth client(토큰을 받아 온다)
    • authorization server : 토큰을 발급한다
  • 인가 자체가 OPTIONAL 이다. 인증(authentication) 없는 공개 서버도 규격 위반은 아니다.

토큰은 한 곳 전용 (audience)

  • 클라이언트는 토큰을 받을 때 resource 파라미터로 "이 서버용"을 박는다.
  • 서버는 자기용이 아닌 토큰은 거부한다. 그래야 다른 서비스용 토큰을 주워 와 재사용하는 공격이 막힌다.
  • 서버가 뒤에서 제3자 API 를 부르면, 서버는 그 API 의 OAuth client 가 되어 새 토큰을 받는다. 받은 토큰을 그대로 넘기면(passthrough) 업스트림이 "MCP 서버가 검증했겠지"라고 믿어 버린다. 그러면 통제(control)·감사·신뢰 경계가 다 깨진다.

scope 는 점진적으로 올린다

  • 처음에는 읽기 수준 최소 scope 를 받는다. 권한이 더 필요한 작업을 처음 하려 할 때 서버가 WWW-Authenticate 로 필요한 scope 를 알려 준다. 그때 올린다.

규칙과 판단

클라이언트는 resource 파라미터로 대상을 박고, MCP 서버는 자기 앞으로 발급된 토큰만 받으며, 업스트림은 따로 발급받은 토큰으로 부르고 받은 토큰을 그대로 넘기면 안 된다는 단계 흐름.
그림으로 보기 (AI 생성 · Codex · 누르면 크게)

갈림길

① 전송으로 가르기 [3.2①]

전송이 무엇인가?인가(authorization)는 선택(OPTIONAL)
stdio (같은 머신 프로세스)

환경에서

자격증명은 환경(환경 변수)에서 받는다. 인가 규격은 따르지 않는다 (SHOULD NOT)

HTTP (Streamable HTTP 등)

OAuth 2.1

OAuth 2.1 흐름으로 받은 액세스 토큰을 쓴다. 규격을 따른다 (SHOULD)

그 밖

그 프로토콜 보안 관행

그 밖 전송은 그 프로토콜의 보안 관행을 따른다 (MUST)

②③ 토큰 경계 [3.2②]

토큰의 어느 순간인가?
클라이언트가 토큰을 요청할 때

resource 파라미터

클라이언트는 토큰을 요청할 때 resource 파라미터(RFC 8707)로 대상 서버를 박는다. 그래서 다른 서비스에 못 쓴다

MCP 서버가 받을 때
audience 에 자기가 드나?
예

받는다

서버는 자기 앞으로 발급된(audience 에 자기가 든) 토큰만 받는다

아니오

거부한다

나머지는 거부한다. 무효·만료 토큰에는 HTTP 401 을 준다

업스트림을 부를 때

따로 발급받은 토큰

서버가 그쪽의 OAuth client 가 되어 따로 발급받은 토큰을 쓴다. 받은 토큰을 그대로 넘기기(token passthrough)는 금지다 (MUST NOT). 넘기면 업스트림의 rate limit·요청 검증이 우회된다. 로그에 누가 불렀는지 안 남는다

④ scope 크기 [3.2②]

언제인가?
처음

최소 scope

처음에는 읽기·탐색용 최소 scope 를 받는다

권한 필요한 작업을 처음 할 때

요구(scope challenge)하면 올린다

서버가 WWW-Authenticate 로 필요한 scope 를 요구(scope challenge)하면 올린다. files:*·admin:* 를 한 번에 받으면 토큰 탈취 피해가 커진다

흐름

그림 속 이름·금액·시간·토큰 수는 예시예요. 공식 한도·동작은 카드 본문 값이 기준.

장면 글로 보기

칸 : MCP 클라이언트 → MCP 서버 → 업스트림

블록 : 액세스 토큰 (바뀌는 것)

규칙 (MCP 서버) : 자기 앞으로 발급된(audience 에 자기가 든) 토큰만

질문 : 서버가 받은 토큰을 업스트림에 내밀어도 될까?

① 따로 발급받는다 ✓

  1. 시작 : MCP 클라이언트 — 쪽지 : 토큰 요청 / resource= / https://mcp.shop.example
  2. 요청 : 액세스 토큰 → MCP 서버 : resource(RFC 8707)로 대상을 박는다 — 쪽지 : Authorization: / Bearer eyJ… / aud: mcp.shop.example
  3. 검사 : MCP 서버 : audience 에 자기가 든 토큰만 받는다 — 쪽지 : aud 일치 → 받음
  4. 업스트림 호출 : MCP 서버 → 업스트림 : 따로 발급받은 토큰을 쓴다 — 쪽지 : Authorization: / Bearer sf_9x… / (Salesforce 가 발급)
  5. 끝 : 서버가 그쪽의 OAuth client 가 되어 따로 발급받은 토큰으로 부른다

② 그대로 넘긴다 ≠

  1. 시작 : MCP 클라이언트 — 쪽지 : 토큰 요청 / resource= / https://mcp.shop.example
  2. 요청 : 액세스 토큰 → MCP 서버 : 토큰을 받는다 — 쪽지 : Authorization: / Bearer eyJ… / aud: mcp.shop.example
  3. 검사 : MCP 서버 : audience 에 자기가 든 토큰만 받는다 — 쪽지 : aud 일치 → 받음
  4. 넘기기 : MCP 서버 → 업스트림 : 그대로 넘긴다(passthrough) — 쪽지 : Authorization: / Bearer eyJ… / (받은 그 토큰)
  5. 끝 : 받은 토큰을 그대로 넘기면 업스트림의 rate limit·요청 검증이 우회된다. 로그에 누가 불렀는지 안 남는다

나머지 규칙

  1. 그 밖의 필수

    • PKCE(S256)를 쓴다
    • 토큰은 Authorization 헤더로 보낸다(URL 쿼리 금지)
    • 짧은 수명 토큰을 권장한다
    • 공개 클라이언트는 refresh 토큰을 회전한다
  2. Claude API 쪽

    • MCP connector : authorization_token 에 액세스 토큰을 넣는다. OAuth 흐름과 갱신은 호출 전에 우리가 처리한다. 자세한 것은 ■ MCP connector 에 있다
    • Claude API 호출 자체 : Authorization: Bearer <API 키 또는 짧은 수명 토큰> 을 쓴다. x-api-key 는 레거시 대체 수단(legacy fallback)으로 아직 지원된다. 짧은 수명 토큰은 Workload Identity Federation(2026-05-04 출시)이다
    • 에이전트가 키를 아예 못 보게 하려면 프록시가 주입한다. 자세한 것은 ■ 프롬프트 인젝션 피해 경로 끊기 에 있다
규칙 원문 ①②③④ (갈림길로 그린 규칙)
  1. 전송별로 다르다 [3.2①]

    전송자격증명규격 말
    stdio (같은 머신 프로세스)환경(환경 변수)에서 받는다인가 규격을 따르지 않는다 (SHOULD NOT)
    HTTP (Streamable HTTP 등)OAuth 2.1 흐름으로 받은 액세스 토큰규격을 따른다 (SHOULD)
    그 밖그 프로토콜 보안 관행MUST
  2. 토큰 경계는 audience 다 [3.2②]

    • 서버는 자기 앞으로 발급된(audience 에 자기가 든) 토큰만 받고 나머지는 거부한다. 무효·만료 토큰에는 HTTP 401 을 준다
    • 클라이언트는 토큰을 요청할 때 resource 파라미터(RFC 8707)로 대상 서버를 박는다. 그래서 다른 서비스에 못 쓴다
    • 업스트림(Salesforce 등)을 부를 때는 서버가 그쪽의 OAuth client 가 되어 **따로 발급받은 토큰**을 쓴다. 받은 토큰을 그대로 넘기기(token passthrough)는 금지다 (MUST NOT)
  3. passthrough 가 왜 나쁜가 : 까닭은 넷이다

    • 업스트림의 rate limit·요청 검증이 우회된다
    • 로그에 누가 불렀는지 안 남는다
    • 훔친 토큰으로 서버를 데이터 유출 통로로 쓴다
    • 한 서비스가 뚫리면 옆 서비스까지 뚫린다
  4. scope 도 최소부터 받는다. 처음에는 읽기·탐색용 최소 scope 를 받는다. 권한이 필요한 작업을 처음 할 때 서버가 WWW-Authenticate 로 필요한 scope 를 요구(scope challenge)하면 올린다

    files:*·admin:* 를 한 번에 받으면 토큰 탈취 피해가 커진다. 자세한 것은 ■ 최소 권한 에 있다

더 깊이 : 판단 규칙 · 상황 변수 (설명)

판단 규칙

조건답근거
stdio 서버에 자격증명을 준다환경(환경 변수)에서 준다authorization
HTTP 서버에 인가를 붙인다이 규격(OAuth 2.1 기반)을 따른다authorization
인가를 꼭 붙여야 하나선택(OPTIONAL)이다authorization
서버가 받은 토큰의 audience 가 자기가 아니다거부한다 (무효면 401)authorization
서버가 업스트림 API 를 부른다업스트림용 토큰을 따로 받는다. 받은 토큰을 그대로 넘기지 않는다authorization · security best practices
클라이언트가 토큰을 요청한다resource 파라미터로 대상 서버를 명시한다authorization
scope 범위를 정한다최소부터 받는다. 서버가 필요한 scope 를 요구(scope challenge)할 때 올린다security best practices
토큰을 전달한다Authorization 헤더로 보낸다. URL 쿼리는 금지다authorization
connector 로 인증 서버에 붙는다우리가 OAuth 흐름을 먼저 돌려 authorization_token 에 넣는다mcp-connector

상황 변수

변수값답 쪽근거
전송stdio환경 변수를 쓴다판단 규칙 1
전송Streamable HTTPOAuth 2.1 을 쓴다 (붙일 때)판단 규칙 2
서버 성격공개 읽기 전용 문서 서버인가가 없어도 규격 위반이 아니다authorization
서버 성격사용자 데이터를 다룬다OAuth 와 audience 검증을 쓴다authorization
업스트림있다(제3자 API)서버가 별도 OAuth client 가 된다authorization
서버 역할정적 client ID 로 제3자 인가 서버에 중계하는 프록시다동적 등록 클라이언트마다 사용자 동의를 받는다authorization
누가 흐름을 돌리나MCP connector 를 거친다우리 앱이 돌린다 (호출 전 토큰 확보·갱신)mcp-connector
에이전트가 키를 봐도 되나안 된다(인젝션 위험)경계 밖 프록시·MCP 서버가 주입한다secure-deployment · ■ 프롬프트 인젝션 피해 경로 끊기

연습 문제

사내 MCP 서버(Streamable HTTP)가 상담원 앱이 보낸 액세스 토큰을 검사한 뒤, 뒤에서 Salesforce API 를 불러 고객 기록을 가져온다업스트림 호출. 개발자는 "받은 토큰을 그대로 넘기면 사용자 권한이 유지돼 안전하다"고 한다. Salesforce 호출에는 어떤 토큰을 쓸까?

시험 대비

함정

  • "stdio 서버에도 OAuth 2.1 흐름을 붙인다" 는 함정이다. 규격은 SHOULD NOT 이다. 자격증명은 환경에서 받는다
  • "원격 MCP 는 반드시(MUST) OAuth 를 쓴다" 는 함정이다. 인가 자체가 OPTIONAL 이다. HTTP 면 SHOULD 다
  • "로컬 프로세스는 API 키 경계다" (영상 표현) 는 함정이다. 공식은 "환경에서 자격증명" 이라고 한다
  • "토큰을 그대로 넘기면 사용자 권한이 유지돼 오히려 안전하다(SSO 처럼)" 는 함정이다. 규격이 명시적으로 금지한다
  • 용어 : 규격의 'Confused Deputy' 절은 좁게는 정적 client ID 를 쓰는 프록시 서버의 동의 문제다(동적 등록 클라이언트마다 사용자 동의 필수). 토큰 그대로 넘기기는 별도 항목이다. 다만 규격은 그것도 confused deputy 를 부를 수 있다고 적는다
  • "scope 를 한 번에 넉넉히 받아 동의창을 줄인다" 는 함정이다. 과한 scope 는 피해 반경을 키우고, 동의 포기도 부른다. scope 는 점진적으로 올린다

신호

"Authorization is OPTIONAL for MCP implementations."

"Implementations using an STDIO transport SHOULD NOT follow this specification, and instead retrieve credentials from the environment."

"Implementations using an HTTP-based transport SHOULD conform to this specification."

"MCP servers MUST only accept tokens specifically intended for themselves and MUST reject tokens that do not include them in the audience claim"

"The MCP server MUST NOT pass through the token it received from the MCP client."

"Token passthrough is explicitly forbidden in the authorization specification"

보통은 stdio 는 환경, HTTP 는 OAuth 지만 공개 문서 원격 서버는 인가 자체가 선택이고, 로컬 서버가 SaaS 를 불러도 stdio 구간은 환경, 토큰 그대로 넘기기는 여전히 금지다.
그림으로 보기 (AI 생성 · Codex · 누르면 크게) — 자세한 조건은 아래 '더 깊이'에 있어요
더 깊이 : 뒤집히는 때 · 오답 재료 (설명)

뒤집히는 때

1. "원격이니 OAuth 필수"가 틀리는 때

  • 조건 : 사내망 안의 공개 사내 문서 검색 서버다(Streamable HTTP). 사용자별 데이터는 없다.
  • 판단 : 인가는 OPTIONAL 이다. 붙인다면 이 규격을 따른다(SHOULD). "MUST OAuth" 라고 쓴 보기는 과장이다.
  • 근거 : authorization

2. stdio 인데 OAuth 가 등장하는 때

  • 조건 : 로컬 stdio 서버가 뒤에서 원격 SaaS API 를 부른다.
  • 판단 : 클라이언트와 stdio 서버 사이 구간은 환경 자격증명을 쓴다. 서버와 SaaS 사이 구간은 그 SaaS 의 인증 방식을 쓴다(OAuth 일 수 있다). 두 구간을 섞어 "stdio 에도 OAuth 2.1 흐름" 이라 하면 틀린다.
  • 등급 : (stdio 구간) · 업계 일반(업스트림 구간의 방식)
  • 근거 : authorization

3. "사용자 권한 그대로 쓰고 싶다"

  • 조건 : 사용자별 Salesforce 권한을 그대로 적용해야 한다. 그래서 받은 토큰을 넘기자는 제안이 나온다.
  • 판단 : 뒤집히지 않는다. 업스트림 토큰은 업스트림 authorization server 가 따로 발급한다. 사용자 권한을 살리려면 업스트림 쪽 OAuth 에서 사용자 동의로 받는다(방식 세부는 규격 밖).
  • 등급 : (금지) · 업계 일반(대안 방식)
  • 근거 : authorization "The access token used at the upstream API is a separate token, issued by the upstream authorization server."

4. scope 를 크게 받는 게 나아 보이는 때

  • 조건 : 동의창이 여러 번 떠서 사용자가 귀찮아한다.
  • 판단 : 공식은 반대로 말한다. 과한 scope 목록이 오히려 동의 포기를 부른다. 토큰이 새면 피해도 크다. 최소로 받고 점진적으로 올린다. 거절된 scope 는 캐시해 반복 요청을 막는다.
  • 근거 : security best practices

오답 재료

오답왜 끌리나왜 틀리나근거
"stdio 서버에도 OAuth 2.1 흐름"보안이면 무조건 OAuth 라고 여긴다stdio 는 SHOULD NOT 이다. 자격증명은 환경에서 받는다authorization
"원격 MCP 서버는 반드시(MUST) OAuth"원격이면 인증이 필수일 것 같다인가 자체가 OPTIONAL 이다. HTTP 면 SHOULD 다authorization
"받은 사용자 토큰을 업스트림에 그대로 넘기면 권한이 유지돼 안전"SSO 처럼 편하다규격이 명시적으로 금지한다. 통제가 우회되고, 감사가 안 되고, 유출 통로가 된다security best practices
"audience 는 안 봐도 서명만 맞으면 됨"JWT 서명 검증에 익숙하다자기용 토큰인지 꼭 확인해야 한다authorization
"토큰을 URL 쿼리로 넘김"구현이 쉽다금지다. 헤더로 보낸다authorization
"scope 는 처음에 전부 받는다"동의창이 한 번만 뜬다피해 반경이 커지고 동의 포기를 부른다. 점진적으로 올린다security best practices
"Claude API 는 x-api-key 헤더만"옛 예제에 익숙하다지금 기본은 Authorization: Bearer 다. x-api-key 는 레거시 대체 수단이다API overview

참고

더 깊이 : 출처 · 상태 · 미해결 (참고)

출처

상태

  • 고침 1곳(API 키 헤더 — 목록은 x-api-key 로만 적었음)

미해결

  1. 목록 3.2① 은 "Claude API 호출 자체는 API 키(x-api-key 헤더 — 근거 확인 필요)"였다. 지금 docs 는 Authorization: Bearer 가 기본이고, x-api-key 는 "Legacy fallback" 이다. 이 표기가 언제 바뀌었는지는 릴리스 노트에서 못 찾았다(검색어 "x-api-key", "Authorization header"). WIF 출시는 2026-05-04 다(릴리스 노트).
  2. 규격 버전 : 이번 원문은 2025-11-25 버전이다. 시험이 이전 버전(2025-06-18) 기준이면 세부(client ID metadata 문서 등)가 다를 수 있다. 이 카드의 규칙(OPTIONAL · stdio 환경 · audience · passthrough 금지)이 이전 버전에도 있었는지는 확인하지 못했다. 이전 버전을 받지 않았다.
  3. 목록의 "'lethal trifecta'·'Confused Deputy' 용어 주의"는 원문과 맞다. confused deputy 절은 정적 client ID 프록시의 동의 문제다(authorization). passthrough 는 별도 항목이지만, 규격은 그것도 confused deputy 를 부를 수 있다고 적는다.