Fail-close: 모든 AI 에이전트가 기본적으로 갖춰야 할 도구 접근 방식
요약
AI 에이전트 설계 시 'Fail-close(실패 시 차단)' 원칙의 중요성을 강조합니다. 기본적으로 모든 도구 접근을 차단하고 명시적인 권한만 부여함으로써, 모델의 비결정론적 특성으로 인한 보안 사고와 폭발 반경을 제어해야 합니다.
핵심 포인트
- Fail-close는 사후 가드레일이 아닌 설계 단계의 기본값이어야 함
- 기본 허용(Allow-by-default) 방식은 모델의 환각과 파괴적 행동에 취약함
- 도구 접근 권한은 작업 및 컨텍스트 단위로 명시적이고 감사 가능하게 부여해야 함
- 모델의 비결정론적 특성을 제어하기 위해 시스템의 결정론적 제약이 필수적임
Fail-close: 모든 AI 에이전트가 기본적으로 갖춰야 할 도구 접근 방식
저는 16년 중 대부분을 결제 플랫폼을 구축하며 보냈습니다. 그곳에서 어떤 프레임워크나 패턴보다 먼저 내면화하게 되는 첫 번째 원칙은, 안전한 상태란 곧 닫힌 상태(closed state)라는 점입니다. 검증할 수 없는 트랜잭션(transaction)은 "아마 괜찮을 거야"라는 판정을 받지 못하고 거절됩니다. 의존성을 잃은 서비스는 오래된 잔액을 계속 제공하지 않고 중단됩니다. 여러분은 실패 모드가 즉흥적인 대응이 아닌 거절이 되도록 시스템을 설계하는 법을 배우게 됩니다.
그러다 저는 MCP를 통해 실제 도구(tools)와 연결된 AI 에이전트(AI agents)를 구축하기 시작했고, 업계가 그 반대의 기본값을 지향하는 것을 목격했습니다. 에이전트들은 광범위한 도구 접근 권한을 가진 채 출시되며, 기본 허용(allow-by-default) 방식을 취하고, "나중에 가드레일(guardrails)을 추가하겠다"는 모호한 의도를 가집니다. 그러한 본능은 잘못되었으며, 프로덕션(production)에 배포하기 전에 이를 수정하지 않는 팀들에게 큰 대가를 치르게 할 것입니다.
에이전트에게 "fail-close"가 실제로 의미하는 것
Fail-close는 첫 번째 도구가 등록되기 전에 선택해야 하는 기본값이지, 나중에 덧붙이는 가드레일이 아닙니다. 이는 "이 에이전트가 지금 이 도구를 호출할 수 있는가?"라는 질문에 대한 기본 답변이 "아니오"여야 하며, 모든 "예"는 명시적이고, 범위가 지정되었으며, 감사 가능한(auditable) 권한 부여여야 함을 의미합니다.
오늘날 대부분의 에이전트 설정은 그 반대로 동작합니다. MCP 서버 세트를 등록하면 모델은 전체 메뉴를 받게 되고, 환각(hallucination)을 일으킨 계획과 파괴적인 행동 사이를 막아주는 유일한 것은 모델 자체의 판단과, 어쩌면 사용자가 자동 조종 모드로 클릭하게 될 확인 프롬프트(confirmation prompt)뿐입니다. 이는 과속 방지턱이 있는 기본 허용(allow-by-default) 방식일 뿐입니다. 데모에서는 작동할지 모릅니다. 하지만 모델이 "스테이징 데이터베이스를 정리하는" 가장 깔끔한 경로가 원래는 접근해서는 안 되었던 DELETE 명령을 통하는 것이라고 판단하는 순간, 바로 실패하게 됩니다.
Fail-close (실패 시 차단) 방식의 에이전트는 이를 뒤집습니다. 에이전트는 도구 권한이 전혀 없는 상태에서 시작합니다. 접근 권한은 도구별로, 이상적으로는 작업(operation)별로, 그리고 특정 신원(identity)과 컨텍스트(context)를 바탕으로 부여됩니다. 인식되지 않는 서버, 현재 작업 범위를 벗어난 도구, 혹은 운영 환경(production)을 건드리는 것처럼 보이는 파라미터와 같이 무언가 모호한 상황이 발생하면, 시스템은 추측하는 대신 거부하고 그 거부 사실을 드러냅니다.
이러한 차이가 중요한 이유는 모델은 비결정론적(non-deterministic)이지만, 여러분의 폭발 반경(blast radius)은 결정론적이기 때문입니다. 모델이 무엇을 _하지 않을지_에 대해서는 추론할 수 없습니다. 여러분은 오직 주변 시스템이 무엇을 _허용할지_만을 제약할 수 있습니다.
이를 따르지 않을 때 발생하는 문제
이러한 실패는 생소한 것이 아닙니다. 이는 울타리 없이 확률론적 시스템에 결정론적인 권한을 부여했을 때 발생하는 지루하고 예측 가능한 결과입니다.
에이전트에게 동일한 데이터 저장소에 대한 읽기 및 쓰기 권한을 주면, 여러분이 읽기를 원했을 때 결국 쓰기를 수행하게 됩니다. 왜냐하면 "이 기록들을 요약해줘"와 "이 기록들을 업데이트해줘" 사이의 경계는 오직 프롬프트(prompt)에만 존재하며, 프롬프트는 제안일 뿐이기 때문입니다. 에이전트에게 셸(shell) 접근 권한과 파일 시스템 MCP를 넘겨주면, 어느 시점에는 고립된 상태에서는 올바르지만 컨텍스트상으로는 재앙적인 명령을 실행하게 됩니다. 서비스 토큰으로 접근하는 내부 API도 마찬가지입니다. 충분히 혼란스러운 계획은 파괴적인 엔드포인트(endpoint)를 매우 확신에 차서 호출할 것입니다.
이 중 그 어떤 것도 악의나 정교한 인젝션 공격(injection attack)을 필요로 하지 않습니다. 이는 불확실성 속에서 즉흥적으로 행동하는 시스템에, 즉흥적으로 행동하지 않는 도구를 쥐여주었을 때 발생하는 기본 발생률일 뿐입니다. 여기에 에이전트가 흡수하는 신뢰할 수 없는 콘텐츠로부터의 프롬프트 인젝션(prompt injection)까지 더해지면, '기본 허용(allow-by-default)' 태세는 신뢰성의 문제를 넘어 보안 문제로 변질됩니다. 탈출해야 할 샌드박스(sandbox) 자체가 애초에 존재하지 않았다면, 주입된 명령은 샌드박스를 깨고 나올 필요조차 없습니다.
제가 AI 기능을 출시하기 위한 보안 검토를 담당했을 때, 제가 가장 먼저 던졌던 질문은 결코 "모델이 좋은가"가 아니었습니다. 그것은 "이것이 실행할 수 있는 최악의 단일 도구 호출(tool call)은 무엇이며, 누가 그것을 허용하기로 결정했는가"였습니다. 만약 후자의 질문에 대한 답이 "아무도 결정하지 않았다, 그냥 접근 권한이 있었다"라면, 그 기능은 출시되지 않았습니다.
MCP 도구를 위한 거버넌스(Governance) 및 RBAC
먼저 명확히 해야 할 한 가지가 있습니다. MCP 프로토콜 자체는 역할(roles), 권한 부여(authorization), 또는 RBAC(역할 기반 액세스 제어, Role-Based Access Control)를 정의하지 않습니다. MCP는 클라이언트와 서버가 어떻게 대화하는지를 설명할 뿐, 누가 무엇을 호출할 수 있는지를 정의하지 않습니다. 그 제어 평면(control plane)을 구축하는 것은 여러분의 몫이며, 이를 구현하기에 가장 자연스러운 위치는 모델과 서버 사이에 위치하여 모든 tools/call이 나가기 전에 이를 확인하고 거부 여부를 결정할 수 있는 호스트 애플리케이션 계층입니다.
RBAC, 범위가 지정된 서비스 ID(scoped service identities), 그리고 감사 가능한 권한 부여(audited grants)는 이미 프로덕션 환경의 모든 은행 API와 모든 CI 배포 키(deploy key)에서 작동하고 있습니다. 에이전트는 단지 접근 권한을 요청하는 또 다른 비인간 주체(non-human principal)일 뿐입니다. 우리에게는 이미 기계적 장치(machinery)가 있습니다. 우리는 단지 에이전트가 이를 건너뛸 수 있는 특별한 카테고리인 것처럼 가장하는 것을 멈추기만 하면 됩니다.
모든 MCP 도구를 역할 기반 액세스 제어(RBAC) 뒤에 있는 특권 작업(privileged operation)으로 취급하십시오. 에이전트는 특정 ID(identity) 하에서 실행됩니다. 그 ID에는 역할(role)이 있습니다. 그 역할은 특정 환경으로 범위가 지정된, 열거된 특정 도구 작업 세트를 부여합니다. 연구 에이전트(research agent)는 지식 베이스에 대한 읽기 권한을 가지며 쓰기 권한은 갖지 않습니다. 배포 에이전트(deployment agent)는 스테이징(staging) 환경에 대해 좁은 범위의 작업 세트를 가지며, 프로덕션(production) 환경에 대해서는 더 강력한 게이트(gate)가 필요한 별도의, 더 엄격하게 관리되는 권한을 부여받습니다.
다음 세 가지 요소가 이 과정을 단순한 보여주기식(theatre)이 아닌 실제적인 것으로 만듭니다.
권한 부여는 서버 단위가 아닌 작업(operation) 단위로 이루어져야 합니다. 데이터베이스 MCP 서버를 등록한다고 해서 그 전체 표면(surface)을 넘겨주어서는 안 됩니다. 읽기와 쓰기는 서로 다른 권한이며 서로 다른 역할에 속합니다. 이는 분석가에게 쿼리를 실행해야 한다는 이유만으로 프로덕션 쓰기 자격 증명(production write credential)을 절대 주지 않는 것과 같은 이치입니다.
다음은 로깅(Logging)입니다. 모든 도구 호출(tool call)에는 신원(identity), 역할(role), 인자(arguments), 그리고 결정(decision)이 포함됩니다. 사후에 추측이 아닌 기록(trail)을 통해 "이 에이전트가 무엇을 했고, 무엇을 할 수 있도록 허용되었는가"라는 질문에 답할 수 있어야 합니다. 거부된 호출(denied calls)의 급증은 에이전트의 계획이 그 범위(scope)에서 벗어났다는 신호이므로, 거부된 내역 또한 승인된 내역만큼이나 중요합니다.
그리고 위험한 권한 부여(grants)는 활성화되기 전에 사람이나 더 강력한 정책 게이트(policy gate)를 거칩니다. 페일 클로즈(fail-close)의 목적은 모든 것을 영원히 차단하는 것이 아닙니다. 실제 폭발 반경(blast radius)을 가진 모든 행위가 누군가가 자신의 이름을 걸고 의도적으로 내린 결정임을 보장하는 것입니다.
이식된 본능
저는 Claude Code와 Cursor를 포함한 여러 에이전트 클라이언트에서 실행되는 MCP 서버를 관리하고 있습니다. 이를 구축하고 운영하면서, 기본 도구 표면(tool surface)이 얼마나 무심코 넓어지기 쉬운지, 그리고 등록된 서버와 그것이 노출하는 모든 것에 대한 완전한 접근 사이의 마찰(friction)이 얼마나 적은지를 배웠습니다. 편리함은 실재합니다. 하지만 노출(exposure) 또한 실재합니다.
결제 시스템에서 온 신뢰성(reliability)에 대한 본능은 그대로 적용됩니다. 안전한 상태는 닫힌 상태(closed state)이며, 한 번에 하나의 명시적인 권한 부여(explicit grant)를 통해 그 상태에서 벗어날 자격을 얻는 것입니다. 우리는 "에이전트가 아직 그것을 할 수 없다"는 것을 제약 사항으로 취급하는 것을 그만두는 법을 배웠습니다. 프로덕션(production) 환경에서 그것은 바로 핵심 목적입니다.
이러한 변화를 설명하기는 짧고 미루기는 쉽습니다. 첫 번째 사고가 발생한 후가 아니라, 첫 번째 도구를 등록하기 전에 기본값(default)을 선택하십시오. 이 포스트의 나머지 내용은 모두, 비용이 적게 들 때 그 하나의 결정을 올바르게 내린 결과일 뿐입니다.
출처
- Model Context Protocol — Security Best Practices — 동의 (consent), 혼동된 대리인 (confused-deputy) 위험, 그리고 호스트 측 제어 (host-side controls)에 관한 프로토콜의 공식 가이드라인.
- Model Context Protocol — Tools — 도구 (tools)를 모델이 제어하는 것으로 정의하며, 신뢰 경계 (trust boundaries)와 인간 참여형 확인 (human-in-the-loop confirmation)을 요구함.
- OWASP — LLM01: Prompt Injection — 신뢰할 수 없는 콘텐츠가 왜 에이전트의 동작을 재지정할 수 있는지에 대한 설명.
- OWASP — LLM06: Excessive Agency — 에이전트에게 과도한 기능, 권한 또는 자율성을 부여할 때 발생하는 폭발 반경 (blast-radius) 위험; 최소 권한 범위 지정 (least-privilege scoping)을 권장함.
- NIST — Least Privilege (glossary) — 작업별, 역할 범위별 도구 부여의 근간이 되는 원칙인 최소 권한 (Least Privilege) 원칙.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기