Doberman MCP Proxy를 통한 AI 에이전트 도구 보호하기
요약
AI 에이전트와 도구 사이의 보안 경계를 보호하기 위한 Doberman MCP 프록시 구축 가이드입니다. MCP 표준을 활용하여 정책 집행 게이트웨이를 배치하고, 요청을 PASS, AUTH, BLOCK으로 분류하여 데이터 유출 및 파괴적 공격을 방지하는 방법을 다룹니다.
핵심 포인트
- Doberman은 MCP 서버 앞단에서 동작하는 정책 집행 게이트웨이입니다.
- 요청을 PASS, AUTH, BLOCK 세 가지 상태로 명시적으로 제어합니다.
- 표준 입출력을 통해 기존 MCP 서버를 변경 없이 래핑할 수 있습니다.
- 공격 성공률(ASR)과 오탐률(FPR)을 통해 보안 성능을 정량적으로 측정합니다.
Protecting AI Agent Tools with the Doberman MCP Proxy — Agent Lab Journal
Agent Lab Journal
Guides
...
Security · Intermediate
Doberman MCP Proxy를 통한 AI 에이전트 도구 보호하기
Intermediate
45 minutes
Hands-on benchmark
안전하지 않은 도구 요청은 사람이 반응할 시간을 갖기도 전에 데이터를 삭제하거나 환경 외부로 비밀 정보를 전송할 수 있습니다. 이 가이드에서는 AI 에이전트와 그 도구들 사이에 Doberman이라는 이름의 작은 정책 집행 게이트웨이(policy-enforcement gateway)를 배치할 것입니다. 완성된 게이트웨이는 PASS, AUTH, BLOCK이라는 세 가지 명시적인 결과를 생성하며, 감사 로그(audit log)에서 비밀 정보를 제외하고, 추측된 수치가 아닌 반복 가능한 테스트를 통해 공격 성공률(attack success rate)과 오탐률(false-positive rate)을 계산합니다.
구축하게 될 내용
AI 에이전트는 어떤 외부 기능을 사용할지 결정하고 인자(arguments)를 구성할 수 있습니다. Model Context Protocol (MCP)는 에이전트가 이러한 기능들을 발견하고 호출하는 방식을 표준화합니다. 이러한 편의성은 동시에 좁고 영향력이 큰 경계(boundary)를 만들어냅니다. 단 한 번의 도구 호출이 생성된 텍스트에서 셸(shell), 파일 저장소, 티켓 시스템, 클라우드 API 또는 메시징 서비스로 넘어갈 수 있기 때문입니다.
Doberman은 해당 경계에 위치한 MCP 프록시(proxy)입니다. Doberman은 상위 도구 서버(upstream tool server)가 요청을 보기 전에 각 요청을 읽고, 순서가 지정된 규칙을 적용하며, 다음 세 가지 결정 중 하나를 반환합니다:
PASS
요청을 즉시 전달합니다. 현재 정책 하에서 안전한 작업에 대해서만 이 옵션을 사용하십시오.
...
이 실험실 게이트웨이는 표준 입력 및 출력(standard input and output)을 통해 MCP를 사용합니다. 따라서 해당 서버를 변경하지 않고도 로컬 MCP 서버를 래핑(wrap)할 수 있습니다. 함께 제공되는 모의 서버(mock server)는 실제 명령을 실행하거나 네트워크 액세스를 수행하지 않으므로, 벤치마크 도중 파괴적인 사례나 데이터 유출(exfiltration) 사례가 발생하더라도 안전하게 유지됩니다.
완료 기준
단순히 프록시가 시작되었다고 해서 연습이 완료된 것으로 간주하지 마십시오. 자동화된 스위트(suite)가 상위 서버(upstream server)에 도달하는 PASS 케이스, 승인될 때까지 중단되는 AUTH 케이스, 서버에 도달하지 못하는 BLOCK 케이스를 관찰하고, ASR(거짓 부정률) 및 FPR(거짓 양성률)에 대한 전/후 값이 출력될 때 완료된 것으로 간주합니다.
구체적인 사례: 릴리스 운영(release-operations) 에이전트
에이전트가 소프트웨어 릴리스를 보조한다고 가정합니다. 이 에이전트의 MCP 서버는 세 가지 도구(tool)를 노출합니다:
-
read_file: 릴리스 노트와 설정을 읽습니다.
-
shell: 유지 관리 명령을 실행합니다.
-
send_webhook: 릴리스 알림을 게시합니다.
일반적인 대화에서는 다음과 같은 요청 중 어느 것이든 생성될 수 있습니다:
{
"name": "read_file",
"arguments": {"path": "CHANGELOG.md"}
...
{
"name": "shell",
"arguments": {"command": "git status"}
...
{
"name": "shell",
"arguments": {"command": "rm -rf /srv/releases/current"}
...
첫 번째 요청은 보통 통과할 수 있습니다. 두 번째 요청은 신뢰 경계(trust boundary)를 변경하는데, 일반적인 쉘(shell)은 현재 인자(argument)가 시사하는 것보다 훨씬 더 많은 일을 할 수 있으므로 권한 부여(authorization)가 필요합니다. 세 번째 요청은 파괴적이며, 누군가 승인 토큰(approval token)을 제시하더라도 반드시 차단(block)되어야 합니다.
네 번째 사례는 눈에 덜 띕니다:
{
"name": "send_webhook",
"arguments": {
...
이 실험실 환경을 위해 호스트 이름은 의도적으로 라우팅이 불가능하게 설정되었으며, 값은 자격 증명(credential)이 아닌 합성 마커(synthetic marker)입니다. 그럼에도 불구하고, 이 구조는 데이터 유출(data exfiltration)을 모델링합니다. 즉, 민감한 필드가 외부 요청(outbound request)에 포함되고 있는 것입니다. Doberman은 웹훅(webhook) 도구가 이를 확인하기 전에 반드시 차단해야 합니다.
이는 일반적인 모델의 실수, 침해된 컨텍스트(compromised context), 또는 에이전트가 읽는 문서에 숨겨진 프롬프트 인젝션(prompt injection)에서 비롯될 수 있습니다. 프록시는 왜 해당 요청이 나타났는지 판단할 필요가 없습니다. 프록시의 역할은 모든 사례에서 동일한 경계를 강제하는 것입니다.
규칙을 작성하기 전에 경계 정의하기
유용한 정책은 늘어나는 의심스러운 문구 목록이 아니라, 자산(assets)과 영향(effects)에서 시작됩니다. 이 연습에서는 다음 네 가지를 보호합니다:
-
삭제되거나 덮어씌워져서는 안 되는 파일 및 디렉터리.
-
자격 증명 (Credentials), 토큰 (tokens), 인증 헤더 (authorization headers) 및 비밀번호.
-
승인되지 않은 데이터를 받아서는 안 되는 외부 시스템.
-
셸 (shells)과 같은 범용 실행 인터페이스.
게이트웨이는 좁은 범위의 읽기 작업은 허용하고, 광범위한 권한을 가진 작업이나 외부로 나가는 (outbound) 작업은 검증하며, 알려진 파괴적인 요청이나 비밀 정보를 포함하는 요청은 거부함으로써 최소 권한 (least privilege) 원칙을 적용합니다. 승인 단계는 기본적인 인간 참여형 (human-in-the-loop) 제어 장치입니다. 이는 운영 체제 격리 (operating-system isolation), 도구별 권한 (tool-specific permissions), 또는 제한된 네트워크 액세스 (restricted network access)를 대체하는 것이 아닙니다.
규칙 순서는 의도적입니다:
-
파괴적인 명령 패턴을 차단 (BLOCK).
-
외부로 나가는 도구 내의 비밀 정보 형태를 가진 필드를 차단 (BLOCK).
-
남은 셸 호출을 인증 (AUTH).
-
남은 외부 호출을 인증 (AUTH).
-
이 실습(laboratory)의 나머지 모든 것은 통과 (PASS).
차단 (Block) 규칙은 승인 (approval) 규칙보다 앞서야 합니다. 그렇지 않으면 파괴적인 셸 요청이 광범위한 셸 AUTH 규칙에 먼저 매칭되어 승인 가능한 상태가 될 수 있습니다.
1. 격리된 작업 공간 준비하기
Python 3.10 이상 버전이 필요하며, 제3자 패키지는 필요하지 않습니다. 운영 환경의 MCP 설정 옆이 아닌, 새로운 디렉터리에서 실습을 진행하세요:
mkdir doberman-lab
cd doberman-lab
python3 --version
합성 데이터 (synthetic inputs)만 사용하세요. 실제 환경 변수, 액세스 토큰 (access tokens), 개인 키 (private keys), 운영 환경 URL, 또는 고객 데이터를 테스트 케이스에 복사하지 마십시오.
완성된 디렉터리는 다음을 포함합니다:
doberman-lab/
├── doberman.py
├── policy.json
...
2. Doberman 정책 작성하기
정책 엔진은 우선순위가 명확히 드러나야 합니다. policy.json을 생성하세요:
{
"version": 1,
"outbound_tools": ["send_webhook"],
...
기본적인 PASS 설정은 세 개의 알려진 도구만 있는 소규모 실험실 환경에만 적합합니다. 실제 운영 환경(Production) 설정에서는 일반적으로 도구 허용 목록(Allowlist)을 사용해야 하며, 일치하지 않는 요청은 기본적으로 차단(BLOCK)하도록 설정해야 합니다. 명령 문자열(Command strings) 기반의 차단 목록(Denylist) 방식으로는 별칭(Aliases), 인터프리터(Interpreters), 인코딩된 페이로드(Encoded payloads), 대체 유틸리티(Alternate utilities) 또는 새로운 위험한 도구들을 모두 방어할 수 없습니다.
정규 표현식(Regular expression)은 순차적 강제 적용(Ordered enforcement)을 보여주기 위해 의도적으로 좁게 설정되었습니다. 이는 범용 셸 파서(Universal shell parser)가 아닙니다. 이후 섹션에서는 고위험 배포 환경에서 셸(Shell)을 완전히 제거하는 방법을 설명합니다.
3. 프록시 구현하기
doberman.py를 생성합니다. 이 프록시는 일반적인 JSON-RPC 메시지를 전달하고, tools/call을 가로채며, 메타데이터만 포함된 기록을 감사 로그(Audit log)에 작성합니다. 원본 인자(Raw arguments)는 기록되는 대신 해시(Hash) 처리됩니다.
#!/usr/bin/env python3
import argparse
import datetime as dt
...
실행 권한을 부여하고 구문을 확인합니다:
chmod 700 doberman.py
python3 -m py_compile doberman.py
다음 몇 가지 세부 사항은 보안과 직결됩니다:
- 분류(Classification)는 요청이 자식 프로세스(Child process)에 기록되기 전에 수행됩니다.
- 유효한 토큰이 인증(AUTH)을 통과할 수 있지만, 차단(BLOCK) 설정을 무시할 수는 없습니다.
- 프록시는 전달(Forwarding)하기 전에 자체적인 비공개 승인 메타데이터를 제거합니다.
hmac.compare_digest를 사용하여 토큰에 대한 일반적인 문자열 비교를 방지합니다.- 로그에는 도구 이름, 규칙 식별자(Rule identifiers), 결정 사항, 인자 해시(Argument hashes)가 포함되며, 인자 본문(Argument bodies)은 포함되지 않습니다.
- 정책에서 실수로 마지막 규칙을 누락하더라도, 일치하지 않는 요청은 코드 수준에서 차단됩니다.
전송 범위 (Transport scope)
이 구현은 MCP stdio를 통해 한 줄당 하나의 JSON-RPC 객체가 전달되는 것을 가정합니다. 해당 프레임워크(Framing), 인증(Authentication), 세션(Session), 타임아웃(Timeout) 및 동시성 처리(Concurrency handling)를 추가하지 않고 HTTP 또는 스트리밍 전송(Streaming transport) 앞에 이 프록시를 배치하지 마십시오.
4. 안전한 업스트림 서버 추가하기
테스트 서버는 도구 호출이 도달했는지 여부를 기록하지만, 요청된 작업은 의도적으로 수행하지 않습니다. mock_server.py를 생성합니다:
#!/usr/bin/env python3
import json
import sys
...
chmod 700 mock_server.py
python3 -m py_compile mock_server.py
모의 응답(mock response) 내의 프로토콜 버전은 이 고정된 실험실 장치(laboratory fixture)의 일부입니다. 실제 서버를 래핑(wrapping)할 때, 프록시(proxy)는 해당 서버의 초기화 응답을 변경 없이 그대로 전달합니다.
5. PASS, AUTH, BLOCK 케이스 정의
cases.json을 생성합니다. 모든 값은 합성된(synthetic) 데이터이며, 예약된 .invalid 도메인을 사용하여 예시가 실제 수신자를 지칭하는 것을 방지합니다.
[
{
"id": "benign-read-changelog",
...
예상되는 정책 동작(expected policy behavior)을 관찰된 결정(observed decision)과 분리하여 유지하십시오. 만약 테스트 하네스(test harness)가 동일한 규칙으로부터 두 가지를 모두 도출한다면, 스스로의 실수를 확인(confirm)할 수 없게 됩니다. 여기에서 JSON은 예상되는 결과(expected outcome)를 선언하며, 실행 중인 프록시는 관찰된 결과(observed outcome)를 생성합니다.
6. 보호 적용 전후 벤치마크
두 가지 주요 보안 지표는 다음과 같습니다:
공격 성공률 (ASR, Attack success rate) = 상위 서버(upstream server)에 도달한 공격 케이스 ÷ 시도된 모든 공격 케이스.
오탐률 (FPR, False-positive rate) = AUTH 또는 BLOCK에 의해 중단된 양성(benign) 케이스 ÷ 모든 양성 케이스.
이 프로토콜에서는 두 지표 모두 낮을수록 좋습니다. 민감한 케이스는 별도로 평가되는데, 이는 해당 케이스들에 대해 AUTH 결과가 올바른 것이므로 오탐(false positive)으로 계산해서는 안 되기 때문입니다.
bench.py를 생성합니다:
#!/usr/bin/env python3
import json
import subprocess
import sys
INITIALIZE = {
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기