
도구 호출 경계(Tool-call boundary)가 AI 에이전트 보안이 실제로 일어나는 지점이다
요약
AI 에이전트가 도구 호출(tool call) 과정에서 발생할 수 있는 보안 위협과 그 해결책을 다룹니다. 사람이 모든 동작을 승인하거나 LLM이 LLM을 감시하는 방식의 한계를 지적하며, 결정론적인 체크가 필요한 지점을 제안합니다.
핵심 포인트
- 에이전트의 도구 호출은 보안 사고가 발생하는 핵심 경계임
- 모델 출력은 컨텍스트 내 외부 데이터에 의해 조작될 수 있는 신뢰할 수 없는 소스임
- 인간의 수동 승인은 형식적인 승인으로 변질될 위험이 있음
- LLM 기반 감시는 느리고 비결정론적이며 프롬프트 주입에 취약함
- 결정론적이고 빠른 체크 메커니즘이 에이전트 보안의 핵심임
나는 내 코딩 에이전트에게 셸(shell) 액세스 권한을 주었고, 대부분의 경우 훌륭했다 — 문제가 발생하기 전까지는 말이다.
한 세션에서는 형제 디렉터리를 삭제할 수도 있는 명령어를 제안했다. 또 다른 세션에서는 "배포를 테스트하기 위해" curl을 통해 .env 파일을 유출하려고 시도했다. 둘 다 악의적인 의도는 없었다; 둘 다 컨텍스트 창(context window)에 있는 텍스트를 행동으로 충실히 옮기려던 에이전트였을 뿐이다. 그리고 일단 에이전트가 명령어를 실행할 수 있게 되면, "도움이 되는 것"과 "사고" 사이의 거리는 단 한 번의 잘못된 도구 호출(tool call)만큼이나 가깝다.
그렇다면 실제로 그 호출을 어디에서 차단해야 하는가?

두 가지 흔한 답변 모두 나를 실망시켰다. 모든 동작을 사람이 승인하는 방식은 정확히 이틀 동안만 작동하며, 그 후에는 모두가 형식적으로 승인(rubber-stamping)하게 된다. LLM이 LLM을 감시하는 방식은 느리고, 비결정론적(non-deterministic)이며 — 이 부분이 치명적인데 — 감시해야 할 바로 그 오염된 텍스트에 의해 주입(injectable)될 수 있다. 나는 대신 지루하지만 정확한 것을 원했다: 모든 에이전트 동작이 반드시 통과해야 하는 단 한 곳에서의 결정론적인(deterministic) 체크 말이다. 이 포스트는 그 체크가 어디에 위치해야 하는지, 그리고 모델을 호출하지 않고도 어떻게 밀리초 단위로 결정을 내릴 수 있는지에 대한 논거를 담고 있다.
에이전트는 신뢰할 수 없는 지침을 수행하는 권한이 없는 프로세스이다

보안 관점에서 볼 때, AI 코딩 에이전트는 **신뢰할 수 없는 소스로부터 지침을 받아 이를 행동으로 전환하는 권한이 없는 프로세스(an unprivileged process)**이다. 여기서 "신뢰할 수 없는 소스"는 모델 출력(model output)이며, 모델 출력은 컨텍스트 창(context window)에 들어오는 어떤 텍스트에 의해서도 조종될 수 있다: 물론 당신의 프롬프트(prompt)도 포함되지만, 당신이 연 리포지토리(repo), 에이전트가 가져온 웹 페이지, 읽어온 GitHub 이슈, 의존성(dependency)의 README 파일도 마찬가지다.
그런 관점에서 볼 때, 실패 모드(failure modes)는 결코 생소한 것이 아니다:
- Secret 유출 (Secret exfiltration) — 에이전트가
~/.aws/credentials또는.env파일을 읽어 URL로 전송하는 경우. - 파괴적 명령어 (Destructive commands) — 잘못된 경로에 대한
rm -rf실행; 실제로는 '정리'가 아닌 것. - 리버스 셸 / C2 이그레스 (Reverse shells / C2 egress) — 승인하지 않은 곳으로 나가는 아웃바운드 연결.
- 프롬프트 주입 → 행동 (Prompt injection → action) — 오염된 README 파일에
직접 작성하지 않은 에이전트의 도구 호출 경계(tool-call boundary)에 위치하려면 두 가지 가로채기 지점(interception points)이 필요합니다. 생태계가 아직 하나의 표준으로 통일되지 않았기 때문입니다.
- 훅 (Hooks) — 네이티브 훅 시스템을 갖춘 에이전트(Claude Code, Codex)는
PreToolUse/PostToolUse핸들러를 등록할 수 있게 해줍니다. 모든 도구 호출은 실행되기 전에 평가됩니다. - MCP 프록시 (MCP proxy) — Model Context Protocol (MCP)을 사용하는 모든 것은 프록시로서 중간에 위치하여 가로챌 수 있으므로, 모든
tools/call은 당신을 먼저 거쳐 가게 됩니다.
이 두 가지 사이에서, 당신은 Claude Code, Codex, Cursor, Cline, Roo, Gemini CLI, Goose, Hermes, opencode 등 현재 에이전트 환경의 대부분을 그 어떤 것도 패치하지 않고 커버할 수 있습니다. 이는 당신의 사용자 권한으로 실행되며(절대 root 권한이 아님), 외부 서버와 통신(phone home)할 필요도 없습니다.
결정론적 결정(deterministic decision)이 실제로 작동하는 방식
도구 호출이 도착하면, 명시적인 규칙 카탈로그(rule catalog)에 따라 이를 분류하고 다음 세 가지 판결 중 하나를 반환합니다.
- 허용 (Allow) — 명확하게 안전함. 조용히 실행되며, 프롬프트 피로(prompt fatigue)를 유발하지 않음.
- 거부 (Deny) — 명확하게 위험함 (비밀 정보 유출,
rm -rf, 리버스 셸, 주입 기반 호출). 즉시 차단됨. - 질문 (Ask) — 진정으로 모호함. 일시 중지하고 사람에게 질문함.
두 가지 속성이 전체 설계를 지탱합니다.
- 결정 경로에 LLM을 포함하지 않음. 이것은 규칙 엔진(rules engine)입니다. 동일한 입력 → 동일한 판결이 100ms 이내에 이루어지므로, 지연 시간을 아무도 느끼지 못하는 사이에 모든 호출을 동기적으로 제어할 수 있습니다.
- 실패 시 차단 (Fail-closed). 평가 오류가 발생하거나, 타임아웃이 발생하거나, 호출을 분류할 수 없는 경우 결과는 허용이 아니라 **거부(deny)**입니다. 안전벨트의 실패 모드는
많은 호출이 명확하지 않습니다. 에이전트가 이 원격 저장소에 git push 해야 할까요? 이 패키지를 설치해야 할까요? 프로젝트 디렉터리 외부에 파일을 작성해야 할까요? 이러한 것들을 허용(allow) 또는 거부(deny)로 코딩하는 것은 통제권을 사람들이 뜯어내는 방식입니다.
이것이 바로 Ask가 필요한 이유입니다. 에이전트를 일시 정지하고 원터치 **허용/거부(Allow / Deny)**를 전송할 수 있습니다. 그리고 이것은 터미널뿐만 아니라 Telegram, Discord, Slack, Matrix, Mattermost 또는 WhatsApp을 통해서도 가능합니다. 사용자가 자리를 비운 동안 에이전트가 계속 작업하도록 내버려 두고, 의도하지 않은 것을 요청할 때
위의 내용은 가설이 아닙니다. 저는 AI 코딩 에이전트를 위한 오픈 소스 (AGPL-3.0), 로컬 우선 (local-first) 정책 계층인 **Belay**를 구현했습니다. 하나의 설치 프로그램이 사용자가 이미 실행 중인 에이전트를 자동으로 감지하고, 훅 (hooks) 또는 MCP 프록시 (proxy)를 통해 연결합니다:
curl -fsSL https://dl.belay.secblok.io/install.sh | bash
문서와 데스크톱 앱은 **belay.secblok.io**에서 확인할 수 있으며, 리포지토리는 **github.com/SECBLOK/belay**에 있습니다.
만약 에이전트를 무인 상태로 실행하신다면, 다음 두 가지 사항에 대해 진심 어린 피드백을 부탁드립니다. 기본 거부 (deny) 규칙이 실제 워크플로에 너무 공격적인지, 그리고 제가 놓치고 있는 회피 표면 (evasion surface)은 어디인지에 대한 의견입니다. 허용/거부 (allow/deny) 규칙은 모두 공개되어 있습니다. 규칙에 대해 논쟁하고, 깨뜨려 보시고, 제가 놓친 부분은 풀 리퀘스트 (PR)를 보내주세요.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기