AI 에이전트가 '도구를 실행하는 순간'이 진짜 위험 지점이다
AI 에이전트 보안을 이야기할 때 대부분의 팀이 프롬프트 필터나 입력 검증에 집중한다. 합리적인 출발이지만, 실제 사고는 모델이 답변을 생성하는 순간보다 도구를 실행하는 순간에 발생한다. 에이전트가 ../../etc/shadow를 읽거나, 외부 URL로 데이터를 전송하거나, 셸 커맨드를 호출하는 그 순간 — 프롬프트 수준의 방어는 이미 뒤에 있다.
이게 이번 주 두 편의 글이 공통으로 가리키는 핵심이다. velog의 'OPA+MCP+OCSF로 Human Approval 만들기'와 dev.to의 'Put AI Agent Actions Behind an Approval Gate'는 각각 다른 기술 스택과 맥락에서 출발하지만, 결론은 같다. 승인(Approval)은 Boolean 하나가 아니라, 아키텍처다.
review를 반환하는 것과 실행을 제어하는 것은 다르다
velog 글은 OPA 정책 엔진이 allow / review / deny를 반환하는 Phase 1에서 시작한다. 여기서 많은 팀이 멈춘다. deny는 막으면 되고, review는 일단 로그에 남기면 되는 거 아닌가 — 라고 생각하는 순간, Human-in-the-loop는 형식만 남은 절차가 된다.
Phase 2의 설계는 이 간극을 메우는 과정이다. review 판정이 발생했을 때 실제로 작동하는 제어 흐름은 다음과 같다.
- 승인 대기열 연결:
review응답을 받은 Agent Gateway는approval_id만 외부에 노출한다. 원본 인자는 프로세스 메모리에만 보관하고, API 응답이나 trace에 남기지 않는다. - 원자적 상태 선점: 동일한 승인 요청이 동시에 두 번 처리되는 걸 막기 위해, 외부 I/O 이전에 lock 내부에서 상태를
pending → executing으로 전환한다. MCP 호출이 끝난 다음 상태를 바꾸는 게 아니라, 실행 권한을 먼저 선점하는 순서가 중요하다. - 서명된 1회성 Capability 발급: 승인 결과를 단순히
approved: true로 전달하면 공격자가 같은 값을 직접 만들어 우회할 수 있다. 대신tool,argument_fingerprint,approval_id,expires_at을 HMAC-SHA256으로 서명한 capability 토큰을 발급한다. 원본 인자는 토큰에 포함하지 않는다. - MCP 서버 재검증: Gateway 검증만 믿지 않는다. MCP 서버도 capability의 tool 바인딩, 인자 fingerprint, 만료 시각을 독립적으로 검증하고, 동일 capability의 replay는 consumption lock으로 차단한다.
dev.to 설계가 추가로 짚은 것: 계획과 승인의 바인딩
dev.to 글은 다른 각도에서 같은 문제를 본다. '사용자가 Approve를 눌렀는데, 워커가 5초 뒤에 변경된 계획을 받으면?' — 승인이 Boolean 하나라면, 리뷰어가 한 번도 본 적 없는 무언가를 시스템이 승인한 셈이 된다.
제안하는 계약 구조는 명확하다. 승인은 정확한 계획에 제한된 권한을 바인딩해야 한다.
- 브라우저는 읽기 전용 계획을 가져와 그 version과 digest를 함께 POST한다
- API는 digest를 재계산하고, 단일 트랜잭션 안에서 short-lived grant를 생성하며 승인 이벤트를 append한다
- 워커는 외부 액션 전마다 grant를 다시 로드하고, 만료·폐기·계획 불일치를 거부한다
- 계획이 변경되면 409 반환, 중복 승인은 멱등성 처리, 폐기 요청은 다음 액션에서 즉시 거부
중요한 경고도 있다. 승인은 이미 완료된 외부 효과를 자동으로 보상할 수 없다. 롤백은 배포 수준의 문제이고, 이미 실행된 액션의 보상은 도메인에 따라 불가능할 수도 있다. 따라서 각 액션 전에 effect receipt(액션 ID, 인자 hash, 목적지, grant 버전, 시작/종료 시각, 결과)를 먼저 persist하고 진행해야 한다.
팀 적용 시사점: 설계가 빠진 자동화는 통제 환상을 만든다
두 글을 같이 읽고 나서 팀 리드로서 가장 먼저 드는 생각은 이거다. 많은 팀이 승인 UI를 만들고 나서 승인 아키텍처를 설계했다고 착각한다.
실제로 설계해야 할 것들을 체크리스트로 정리하면:
- 승인 주체 명확화: 누가 승인하는가, 승인 대기열은 어디에 persist하는가
- 만료 정책: capability의 TTL은 얼마인가, 만료 후 재요청 흐름은 어떻게 되는가
- 동시성 처리: 동일 요청에 대한 동시 승인을 원자적으로 막고 있는가
- 재사용 방지: replay 공격을 Gateway와 도구 서버 양쪽에서 독립적으로 차단하는가
- 인자 바인딩: 승인 시점의 인자와 실행 시점의 인자가 동일함을 cryptographic하게 보장하는가
- 감사 증거: 도구 인자 원문 대신 fingerprint와 key 목록만 남기고 있는가 (로그가 민감정보 저장소가 되지 않도록)
- 비상 정지: 글로벌 stop이 UI 버튼 숨기기가 아니라 실제 admission 차단으로 구현되어 있는가
velog 구현에서 OCSF 1.8의 ai_operation 프로필을 적용한 것도 팀 관점에서 주목할 만하다. 표준 필드에 억지로 끼워 맞추지 않고, 정책 정보는 unmapped.security에 보존하는 방식은 SIEM 연동 시 오해를 줄이는 실용적인 선택이다.
전망: 에이전트 통제 구조는 이제 팀 필수 인프라다
velog 글의 Phase 3 예고가 의미심장하다. '에이전트의 의도와 OS에서 실제 발생한 행동을 비교'하겠다는 것 — 이건 단순한 정책 체크를 넘어 에이전트 행동 감사의 영역이다. 에이전트가 무엇을 실행하려 했는지와 실제로 무엇이 실행됐는지 사이의 간극을 추적하는 것.
AI-First 워크플로우를 도입하는 팀이라면 이 시퀀스를 지금부터 내재화해야 한다: 정책 판정 → 사람 승인 → 1회성 실행 권한 → 도구 서버 재검증 → 표준화된 증거. 이 다섯 단계 중 하나라도 빠지면 나머지는 통제처럼 보이는 자동화일 뿐이다.
분산 환경으로 확장할 때는 approval ID 소비 상태를 Redis나 DB의 원자 연산으로 관리해야 하고, MCP 인증에는 OAuth 2.1과 audience-bound token이 필요하다. 로컬 랩 수준의 구현이 프로덕션에서 그대로 통하지 않는다는 걸 두 글 모두 솔직하게 인정하고 있다.
AI 에이전트를 팀에 배포하는 타이밍은 각자 다르더라도, 통제 구조 설계는 배포 전에 끝나 있어야 한다. 배포 후에 설계하면 이미 늦다.