20. 권한·동의·human-in-the-loop
🎯 이 장의 목표
- 인증(authentication)과 인가(authorization)를 구분하고, 동의(consent)의 위치를 안다
- human-in-the-loop가 왜 MCP 보안의 핵심 장치인지 이해한다
- 동의 화면·도구 승인을 안전하게 설계하는 원칙을 익힌다
19장이 "누구인가(인증)"였다면, 이 장은 "무엇을 해도 되는가(인가)"와 "사용자가 무엇에 동의했는가(동의)"입니다. MCP는 외부 시스템에 행동을 가하는 통로라, 이 통제가 특히 중요합니다.
세 가지를 구분하기
flowchart LR
A["인증 Authentication\n'누구인가?'"] --> B["인가 Authorization\n'무엇을 해도 되나?'"]
B --> C["동의 Consent\n'사용자가 이 행동을\n허락했나?'"]
classDef a fill:#bfdbfe,stroke:#1d4ed8,color:#000;
classDef b fill:#5eead4,stroke:#0f766e,color:#000;
classDef c fill:#fde68a,stroke:#b45309,color:#000;
class A a;
class B b;
class C c;
- 인증: 호출자가 누구인지 확인 (토큰 검증, 19장).
- 인가: 그 신원이 이 작업을 할 권한이 있는지 (스코프·RBAC). 서버가 내부적으로 집행.
- 동의: 사용자가 이 구체적 행동(이 도구를, 이 인자로)을 하도록 허락했는지. 보통 호스트 UI에서 사람이 결정.
세 가지는 별개입니다. 토큰이 유효(인증)하고 스코프가 충분(인가)해도, 사용자가 "이 파일을 지운다"에 동의하지 않았다면 실행하면 안 됩니다.
human-in-the-loop — MCP 보안의 중심축
10장에서 본 원칙을 보안 관점에서 확장합니다. 도구는 모델이 자동으로 호출하는 primitive입니다. 모델은 틀릴 수 있고, 외부 입력(도구 설명·리소스 내용)에 의해 조종당할 수 있습니다(21장). 그래서 스펙은 도구 호출을 거부할 수 있는 사람이 항상 고리에 있어야 한다(SHOULD) 고 못 박습니다.
애플리케이션(호스트)이 SHOULD 해야 할 것들:
- 어떤 도구가 AI 모델에 노출됐는지 분명히 보여주는 UI 제공.
- 서버를 호출하기 전에 도구 입력을 사용자에게 보여줘 악의적·우발적 데이터 유출을 막기.
- 위험한·되돌릴 수 없는 행동(삭제·결제·외부 전송)에 명시적 확인을 요구.
sequenceDiagram
participant M as 모델
participant H as Host (UI)
participant U as 사용자
participant S as Server
M->>H: 도구 호출 제안 (delete_file, path=...)
H->>U: "이 도구를 이 인자로 실행할까요?" (입력 표시)
alt 사용자 승인
U->>H: 승인
H->>S: tools/call 실행
S-->>H: 결과
else 사용자 거부
U->>H: 거부
H->>M: 거부됨 (실행 안 함)
end
📌 핵심
왜 사람이 필요한가: MCP 서버는 02장에서 본 대로 "호출자를 모릅니다". 모델의 판단도 100%가 아닙니다. human-in-the-loop는 자동화의 편의와 안전 사이의 마지막 안전판입니다. 특히 부작용이 큰 도구일수록 이 확인이 중요합니다.elicitation·sampling과 동의
13장의 클라이언트 기능들도 동의 구조 위에 있습니다.
- elicitation: 서버가 사용자에게 입력을 요청하면, 클라이언트가 UI를 띄우고 사용자가 수락·거절·취소를 고릅니다. 이 자체가 동의 지점입니다.
- sampling: 서버가 호스트 LLM에 추론을 요청할 때, 클라이언트는 보통 사용자 승인을 거쳐 모델에 전달합니다.
- roots: 클라이언트가 서버에 접근 가능한 경로 경계를 알려, 서버가 그 밖을 건드리지 못하게 합니다.
이들은 모두 "서버가 사용자/모델 자원에 함부로 접근하지 못하게" 하는 동의·경계 장치입니다.
동의 화면을 안전하게
2025-07~08에 공개된 실제 취약점들은 동의 흐름의 허점을 드러냈고, 2025-11-25 스펙이 명시적 지침으로 보강했습니다. 동의 UI를 만들 때:
- 클라이언트 신원·리다이렉트 목적지·요청 권한을 명확히 표시하세요(defense in depth — 한 겹이 뚫려도 다음 겹이 막도록 방어를 여러 층 겹치는 "다층 방어" 전략). 사용자가 "무엇에 동의하는지" 알아야 합니다.
- 동적으로 등록된 클라이언트마다 별도 동의 흐름을 거치게 하세요. 프록시용 정적 client ID를 공유하면 위험합니다.
- 쿠키 기반 동의 상태는 깨지기 쉬우니,
__Host-접두 쿠키와 엄격한 SameSite 정책을 씁니다. - 메타데이터 엔드포인트 같은 신뢰할 수 없는 URL을 셸 명령에 그대로 넘기지 마세요 — 검증·정화 필수.
🔒 동의는 "한 번"이 아니다: 06장에서 본 "세션은 인증이 아니다"와 통합니다. 한 번 동의했다고 모든 후속 위험 행동이 자동 승인되는 게 아닙니다. 민감·파괴적 작업은 그때그때 확인하고, 권한도 최소로 시작해 필요 시 step-up(19장)합니다.
권한 설계 — 최소 권한을 코드로
서버는 받은 신원·스코프에 따라 내부적으로 권한을 집행합니다.
PYTHON
# (개념 예) 스코프에 따른 도구 게이팅 @mcp.tool() async def delete_record(record_id: str, ctx: Context) -> str: """레코드를 삭제한다 (파괴적).""" scopes = get_scopes_from_token(ctx) # 인증 미들웨어가 채운 값 if "records:delete" not in scopes: raise PermissionError("이 작업에 필요한 권한이 없습니다") # → 403 계열 # ... 삭제 수행 return "삭제 완료"
- 도구마다 필요한 최소 스코프를 정해 게이팅합니다.
- 파괴적 도구는 스코프 + 호스트의 사용자 확인(human-in-the-loop)을 둘 다 거치게 합니다.
- DB·API 자격은 읽기 전용·범위 한정으로(예: 읽기 전용 커넥션, 스코프된 API 키). 서버는 필요한 자원에만 접근해야 합니다.
🔒 OWASP MCP 가이드 요지(2026-02 공개): 모든 도구 입력을 엄격한 스키마로 검증, 사용자 입력을 셸/SQL/파일에 직접 넘기지 말 것, 서버는 필요한 자원에만 접근(스코프된 키·읽기 전용·제한된 경로), 모든 도구 호출은 호스트에서 명시적 사용자 승인 — 이 권고들이 인가·동의 설계의 뼈대입니다.
이 장에서 배운 것
- 인증(누구)·인가(권한)·동의(이 행동을 허락했나) 는 별개다. 셋 다 통과해야 위험 행동이 정당하다.
- human-in-the-loop는 MCP 보안의 중심축 — 도구 호출은 거부 가능한 사람이 고리에 있어야 하고, 호스트는 입력을 보여주고 위험 행동에 확인을 받는다.
- elicitation·sampling·roots는 동의·경계 장치다. 동의 화면은 신원·리다이렉트·권한을 명확히 표시하고 클라이언트별로 분리한다.
- 서버는 최소 권한으로 내부 인가를 집행하고, 파괴적 도구는 스코프 + 사용자 확인을 함께 건다.
✍️ 확인 문제
- 토큰이 유효하고 스코프도 충분한데도 도구를 바로 실행하면 안 되는 경우는? 무엇이 빠졌나?
- 호스트가 도구를 호출하기 전에 사용자에게 보여줘야 하는 것은 무엇이고, 왜 중요한가?
- "한 번 동의받았으니 이후 삭제 작업도 자동 실행"이 위험한 이유를 [19장]의 step-up 개념과 엮어 설명해 보자.
다음 장: 21. 공격 표면: 프롬프트 주입·도구 중독·혼동된 대리인·공급망