자율 에이전트를 위한 누락된 미들웨어
요약
프론티어 모델의 높은 성능이 오히려 에이전트 아키텍처에서 데이터 프라이버시 문제를 심화시키는 구조적 위험을 분석합니다. 모델이 지시를 너무 충실히 수행하여 불필요한 개인정보를 도구 호출에 포함하거나 컨텍스트에 유출하는 문제를 해결하기 위한 미들웨어 계층의 필요성을 제안합니다.
핵심 포인트
- 모델의 성능 향상이 오히려 데이터 과잉 공유 문제를 야기함
- 도구 입력 시 불필요한 개인정보(PII)가 포함되는 과도한 공유 문제
- API 응답 내 인증 토큰이 컨텍스트에 포함되어 발생하는 유출 위험
- 구조화된 출력 내 개인정보가 로그에 남는 보안 취약점
- 에이전트와 도구 사이에 보안 미들웨어 계층 도입 필요
프론티어 모델(Frontier models)이 어떻게 프라이버시 문제를 애플리케이션의 관심사에서 인프라의 문제로 변화시켰는가
프론티어 모델(Frontier models)은 지시 사항을 충실히 수행합니다. 또한 시스템 경계를 넘어 데이터를 충실히 이동시킵니다. 이는 프라이버시 문제를 애플리케이션의 관심사에서 인프라의 관심사로 변화시키며, 대부분의 에이전트 아키텍처(agent architectures)는 이를 따라잡지 못했습니다.
문제는 모델이 잘못 행동하는 것이 아닙니다. 문제는 모델이 지시받은 대로 정확하게 행동한다는 점이며, 이는 컨텍스트 윈도우(context window)에 있는 모든 데이터 조각이 다음 도구 호출(tool call)로 전송될 후보가 된다는 것을 의미합니다. 금융, 의료, 법률, 인사(HR) 및 보험 시스템에서 이는 이론적인 위험이 아닙니다. 이는 에이전트가 작동하는 방식의 구조적 특성입니다.
이 포스트는 위험이 실제로 어디에서 발생하는지 식별하고, 왜 더 나은 모델이 상황을 악화시키는지 설명하며, 에이전트와 에이전트가 호출하는 모든 도구 사이에 반드시 존재해야 하는 미들웨어(middleware) 계층을 설명합니다.
여행 예약은 저희가 가장 먼저 구현한 도메인입니다. 이 아키텍처는 어디에나 적용 가능합니다.
더 나은 모델, 더 큰 문제
모두가 더 유능한 모델이 위험을 줄일 것이라고 가정합니다. 데이터 처리의 경우 그 반대가 사실입니다.
GPT-3.5는 일관성이 없었기 때문에 우연히 일부 비밀을 보호했습니다. 필드를 누락하거나, 중첩된 구조를 잘못 읽거나, 값을 깔끔하게 추출하는 데 실패하곤 했습니다. 그러한 신뢰할 수 없음은 기능(feature)은 아니었지만, 때때로 기능처럼 작동했습니다.
프론티어 모델(Frontier models)은 그러한 우연을 제거했습니다. GPT-4o, Claude Sonnet, 그리고 Gemini 1.5 Pro는 3,000토큰 규모의 프로필에서 여권 번호를 충실히 추출하여, 그것이 전혀 필요 없는 좌석 선택 도구(seat selection tool)로 직접 전달할 것입니다. 모델이 이렇게 하는 이유는 데이터가 존재하고 지시 사항이 "항공권을 예약하라"고 되어 있기 때문입니다. 모델은 잘못된 행동을 하고 있지 않습니다. 그것이 바로 문제입니다.
세 가지 실패 모드(failure modes)가 있으며, 세 가지 모두 모델이 올바르게 작동하고 있음을 전제로 합니다:
도구 입력에 대한 과도한 공유 (Over-sharing on tool input). 에이전트가 검색을 수행하기 위해 여행자의 전체 프로필을 전달받습니다. 이 프로필에는 여권 번호가 포함되어 있습니다. 검색 도구에는 이 정보가 필요하지 않습니다. 하지만 모델은 여행자의 세부 정보와 함께 도구를 호출하라는 지시를 받았고, 전체 프로필이 곧 여행자의 세부 정보이기 때문에 프로필 전체를 전달합니다. 이제 여권 번호가 도구의 요청 로그(request log)에 나타나게 됩니다.
컨텍스트 내 자격 증명 유출 (Credential leakage in context). 도구가 본문에 세션 토큰(session token)이나 인증 헤더(authorization header)를 포함한 API 응답을 반환합니다. 모델은 이를 자신의 컨텍스트(context)에 포함시킵니다. 이후의 도구 호출에서 모델은 해당 토큰을 참조하거나 반복할 수 있습니다. 이는 환각(hallucination) 실패가 아닙니다. 충실한 재현(faithful reproduction) 실패입니다.
구조화된 출력 내 개인정보 (PII in structured outputs). 예약 확인 도구가 모든 승객 세부 정보, 결제 수단 및 연락처를 포함하는 전체 객체(object)를 반환합니다. 에이전트는 이를 사용자를 위해 요약하지만, 전체 객체는 컨텍스트 창(context window)에 남아 있습니다. 만약 해당 컨텍스트가 로그로 기록된다면, 그 로그에는 예약 확인 밀도의 구조화된 개인정보(PII)가 포함됩니다.
이 중 어느 것도 악의적인 행위자를 필요로 하지 않습니다. 이는 모델이 데이터를 보기 전에 범위(scope)가 지정되었어야 할 데이터에 대해 정상적인 모델 동작이 수행될 때 발생합니다.
아키텍처의 격차 (The Architecture Gap)
전통적인 시스템은 잘 알려진 제어 평면(control plane)을 가지고 있습니다:
Application
↓
API Gateway
...
게이트웨이는 인증(authentication), 속도 제한(rate limiting), 정책 집행(policy enforcement)을 처리합니다. 이는 선택 사항이 아닙니다. 아무도 애플리케이션이 다운스트림 서비스(downstream services)를 직접 호출하게 하여 프로덕션 서비스를 출시하지 않습니다.
에이전트 시스템은 애플리케이션 계층(application layer)과 서비스 계층(service layer)은 재현했지만, 제어 평면(control plane)은 건너뛰었습니다:
Agent
↓
[nothing]
...
에이전트는 도구를 직접 호출합니다. 모든 도구 호출은 에이전트가 컨텍스트에 가지고 있는 모든 것—도구가 받도록 의도되지 않은 데이터까지 포함하여—을 직접 전송하는 과정입니다. 도구는 제공자가 반환하는 모든 것—에이전트가 보지 않도록 의도되지 않은 데이터까지 포함하여—을 반환합니다.
누락된 계층은 도구 경계(tool boundary)에 위치하여 양방향으로 정책을 강제하는 프라이버시 가드(privacy guard)입니다.
Agent
↓
Privacy Guard
...
이는 Envoy 프록시(proxy)나 API 게이트웨이(API gateway)와 동일한 아키텍처 패턴입니다. 다만, 데이터가 HTTP 트래픽이 아닌 구조화된 JSON(structured JSON)이며, 정책이 헤더 기반(header-based)이 아닌 의미론적(semantic)이라는 점에서 구현 방식은 다릅니다. 구조적 역할은 동일합니다. 즉, 데이터가 경계를 넘기 전에 정책이 강제되는 필수적인 초크포인트(chokepoint) 역할을 합니다.
가드가 실제로 수행하는 작업
가드는 양방향의 모든 도구 호출(tool call)을 가로채며 네 가지 작업을 수행합니다:
스캔(Scans): 결정론적 탐지(deterministic detection)를 사용하여 페이로드(payload) 내의 알려진 개인정보(PII) 및 비밀 패턴을 스캔합니다. 여기에는 필드 이름 매칭, 검증을 포함한 정규 표현식 패턴(카드 번호를 위한 Luhn 알고리즘, 구조화된 타입을 위한 형식 검증), 그리고 도메인별 규칙(여행 분야의 예약 참조, 승객 이름 기록(PNR), 로열티 식별자; 금융 분야의 계좌 번호 및 라우팅 코드; 의료 분야의 NPI 번호 및 진단 코드)이 포함됩니다.
결정(Decides): 구성 가능한 정책에 따라 데이터를 통과시키거나, 플레이스홀더(placeholder)로 마스킹(redact)하거나, 결정론적 암호화 토큰(deterministic cryptographic token)으로 교체하거나, 호출을 완전히 차단합니다. 정책은 종류(kind)와 심각도(severity)별로 적용됩니다. 비밀 정보(Secrets)는 차단됩니다. 심각도가 높은 PII는 토큰화(tokenized)됩니다. 일반적인 PII는 마스킹(redacted) 처리됩니다.
감사(Audits): 두 이벤트를 상관시키는 트레이스 ID(trace ID)와 함께 모든 검사, 입력 및 출력을 감사합니다. 감사 이벤트는 민감한 값이나 마스킹된 필드로의 경로를 포함하지 않고, 무엇이 발견되었고 어떤 조치가 취해졌는지를 기록합니다.
강제(Enforces): 호출을 중단시키는 예외(exception) 대신, 에이전트 런타임(agent runtime)이 처리할 수 있는 구조화된 에러(structured errors)로 위반 사항을 노출함으로써 정책을 강제합니다.
구현
우리는 이를 MCP 런타임의 invokeTool() 경계에 통합된 의존성 없는(dependency-free) Node.js 패키지로 구축했습니다. 시작 시 인스턴스를 하나 생성하여 모든 도구 호출에서 공유합니다:
import { createPrivacyGuard } from "@ucp-travel/mcp-privacy-guard";
import { travelDetectors } from "@ucp-travel/mcp-privacy-guard/travel";
...
모든 도구 호출(tool invocation)은 가드(guard)를 두 번 통과합니다:
export async function invokeTool(runtime, toolName, payload, options = {}) {
const requestId = options.requestId || randomUUID();
...
입력 가드(input guard)는 도구 핸들러(tool handler)가 페이로드(payload)를 받기 전에 실행됩니다. 출력 가드(output guard)는 응답이 에이전트의 컨텍스트(context)에 도달하기 전에 실행됩니다. 도구나 에이전트 모두 수정되지 않습니다.
도구별 출력 허용 목록 (Per-Tool Output Allowlists)
도구 응답에 포함된 모든 개인정보(PII)가 문제가 되는 것은 아닙니다. 예약 확인서에는 승객 이름과 연락처 이메일이 포함되어야 합니다. 이를 삭제(redacting)하면 예약 프로세스가 끊어지게 됩니다.
해결책은 어떤 경로(path)가 수정되지 않은 상태로 통과할 수 있는지 지정하는 도구별 출력 허용 목록(per-tool output allowlists)을 사용하는 것입니다:
const OUTPUT_ALLOWLIST_BY_TOOL = {
[MCP_TOOLS.COMPLETE_CHECKOUT]: [
"$.booking.id",
...
해당 도구의 허용 목록에 없는 모든 항목은 전역 정책(global policy)에 따라 삭제(redacted)되거나 토큰화(tokenized)됩니다. 검색 도구는 제공자(provider)의 응답에 개인정보(PII)가 포함되어 있더라도 이를 절대 반환하지 않습니다. 확인 도구는 에이전트가 필요로 하는 정확한 필드만을 반환합니다.
동일한 패턴이 다른 도메인에도 적용됩니다. 뱅킹 에이전트의 계좌 조회 도구는 계좌 별칭과 마지막 4자리를 허용 목록에 포함할 수 있습니다. 의료 에이전트의 예약 도구는 예약 시간과 의료진 이름을 허용 목록에 포함할 수 있습니다. 정책은 도메인별로 다르지만, 메커니즘은 동일합니다.
토큰화 및 키 로테이션 (Tokenization and Key Rotation)
심각도가 높은 개인정보(PII)의 경우, 삭제(redaction)만으로는 충분하지 않습니다. 삭제를 하면 감사 이벤트(audit events) 전반에 걸쳐 데이터를 상관 분석(correlate)할 수 있는 능력을 상실하게 됩니다. 토큰화(Tokenization)는 값을 키 ID(key ID)와 연결된 결정론적인 HMAC 기반 토큰으로 대체합니다:
{ "email": "TOKEN:pii/2026-q3:a3f8c2..." }
이 토큰은 결정론적 (deterministic)입니다. 동일한 입력과 키는 동일한 토큰을 생성하므로, 원래 값을 저장하지 않고도 감사 이벤트 (audit events) 간에 상관관계를 파악할 수 있습니다. 키 ID (key ID)는 토큰을 생성하거나 읽는 모든 이벤트에 기록됩니다. 분기별로 키를 교체(rotate)하더라도 이전 토큰들이 고립되지 않습니다. 해당 토큰이 생성될 당시 어떤 키가 활성화되어 있었는지 정확히 알 수 있기 때문입니다.
키 교체는 환경 설정의 두 줄 변경만으로 이루어집니다. 가드 (guard)는 200 KB 크기 제한 미만의 페이로드 (payload)에 대한 모든 호출에 50ms 미만의 지연 시간만을 추가합니다.
미결 과제 (The Open Question)
토큰화 (Tokenization)는 감사 상관관계 문제를 해결합니다. 하지만 에이전트 추론 (agent reasoning) 문제를 해결하지는 못합니다.
에이전트가 이메일 주소 대신 TOKEN:pii/2026-q3:a3f8c2...를 받게 되면, 해당 주소로 확인 메일을 보낼 수 없습니다. 만약 에이전트가 단순히 값을 확인하는 것이 아니라 해당 값에 따라 동작해야 한다면, 토큰화는 흐름을 끊어버립니다.
현재의 설계는 에이전트가 사용자에게 노출해야 하는 출력 경로 (output paths)를 화이트리스트 (allowlisting)에 등록함으로써 이 문제를 처리합니다. 하지만 에이전트가 원본 값을 보지 않고도 여러 도구 호출 (tool calls)에 걸쳐 내부적으로 값을 사용해야 하는 경우에는 대응하지 못합니다.
그것은 더 어려운 문제입니다. 위임된 복호화 엔드포인트 (delegated decryption endpoint)를 사용하는 가역적 토큰화 (Reversible tokenization)가 한 가지 접근 방식입니다. 어떤 도구가 어떤 종류의 토큰을 받을 수 있는지 제한하는 것이 또 다른 방식입니다. 둘 다 완전히 만족스럽지는 않습니다. 우리는 아직 이를 해결하지 못했습니다.
대부분의 엔지니어링 아티클은 문제가 해결되었다고 주장하며 끝을 맺습니다. 하지만 이 글은 그렇지 않습니다. 왜냐하면 아직 해결되지 않았기 때문입니다. 누락된 미들웨어 (middleware) 계층은 존재하며 오늘날 바로 배포 가능합니다. 에이전트가 평문 (plaintext)으로 본 적 없는 데이터에 대해 어떻게 추론할 것인가라는 질문은 여전히 남아 있으며, 그 해답이 에이전트 시스템 (agentic systems)이 규제 대상 데이터를 대규모로 처리하는 방식을 결정할 것입니다.
Almin Zolotic은 Zologic의 설립자이자 ucp.travel 자율 예약 인프라 (autonomous booking infrastructure)의 저자입니다. 이 글에서 설명하는 아키텍처 — 도구별 허용 목록 (allowlists), 결정론적 토큰화 (deterministic tokenization), 도메인 특화 탐지 (domain-specific detection)를 포함하여, MCP 도구 호출 경계 (tool invocation boundary)에서의 양방향 개인정보 보호 강제 (bidirectional privacy enforcement) — 는 ucp.travel 인프라의 일부로 개발되었으며, 규제 대상 데이터를 처리하는 에이전트 시스템 (agentic systems)을 위한 재사용 가능한 패턴으로서 여기에 기록되었습니다.
ucp.travel은 현재 인간 에이전트가 제공하는 컴플라이언스 (compliance) 특성을 유지하면서도, 예약 루프 (booking loop)에서 인간 에이전트를 제거하고자 하는 운영자를 위한 자율 여행 예약 인프라 (autonomous travel booking infrastructure)입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기