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는 동의·경계 장치다. 동의 화면은 신원·리다이렉트·권한을 명확히 표시하고 클라이언트별로 분리한다.
  • 서버는 최소 권한으로 내부 인가를 집행하고, 파괴적 도구는 스코프 + 사용자 확인을 함께 건다.

✍️ 확인 문제

  1. 토큰이 유효하고 스코프도 충분한데도 도구를 바로 실행하면 안 되는 경우는? 무엇이 빠졌나?
  2. 호스트가 도구를 호출하기 전에 사용자에게 보여줘야 하는 것은 무엇이고, 왜 중요한가?
  3. "한 번 동의받았으니 이후 삭제 작업도 자동 실행"이 위험한 이유를 [19장]의 step-up 개념과 엮어 설명해 보자.
다음 장: 21. 공격 표면: 프롬프트 주입·도구 중독·혼동된 대리인·공급망