본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 16. 05:17

세 명의 AI 어시스턴트, 세 곳의 벤더, 하나의 버그 — 지속적으로 발생하는 '혼란스러운 대리인 (Confused Deputy)' 패턴

요약

Microsoft 365 Copilot, GitHub Copilot, GitLab Duo에서 공통적으로 발견된 '혼란스러운 대리인(Confused Deputy)' 보안 패턴을 분석합니다. 공격자가 콘텐츠 내에 숨겨진 지시사항을 삽입하여 AI가 권한을 남용해 비밀 정보를 유출하게 만드는 메커니즘을 다룹니다.

핵심 포인트

  • 서로 다른 벤더의 AI 서비스에서 동일한 데이터 유출 패턴 발견
  • 숨겨진 지시사항(Unicode, Markdown 등)을 통한 프롬프트 인젝션 공격
  • AI가 컨텍스트 내의 데이터를 명령으로 오인하는 구조적 취약점
  • 자연어 특성상 기존의 입력 유효성 검사로 방어하기 어려움

저는 LLM 앱에서 데이터가 유출된 공개 사례들을 수집해 왔는데, 저를 놀라게 한 점은 이러한 일이 발생한다는 사실 그 자체가 아니라, 그 사례들이 얼마나 동일한 형태를 띠고 있는가 하는 점이었습니다. 서로 다른 기업, 서로 다른 제품임에도 불구하고 정확히 같은 형태를 보입니다. 만약 당신이 LLM 앱을 구축한다면, 이 패턴은 사라지지 않을 것이기에 반드시 머릿속에 각인해 둘 가치가 있습니다.

다음은 공개적으로 알려지고 패치된 세 가지 사례입니다:

  • EchoLeak (Microsoft 365 Copilot, CVE-2025-32711로 공개됨, CVSS 9.3) — 숨겨진 지시사항이 포함된 단 하나의 수신 이메일이 사용자의 클릭 없이도 Copilot이 내부 컨텍스트를 추출하도록 만들었습니다.
  • CamoLeak (GitHub Copilot Chat, CVE-2025-59145로 공개됨, CVSS 9.6) — 풀 리퀘스트 (Pull Request) 내의 보이지 않는 마크다운 (Markdown) 주석이 Copilot으로 하여금 프라이빗 저장소 (Private Repos)에서 비밀 정보를 가져와 인코딩하여 외부로 내보내게 했습니다.
  • GitLab Duo (duo-ui!52에서 패치됨) — 머지 리퀘스트 (Merge Request) 설명, 커밋 (Commit), 또는 소스 코드에 숨겨진 지시사항이 (Claude를 기반으로 구축된) Duo가 프라이빗 소스를 유출하도록 만들었습니다.

이들이 공유하는 형태
벤더를 제외하고 나면, 각 사례는 동일한 다섯 단계의 흐름을 가집니다:

  1. AI가 신뢰 경계 (Trust Boundary) 외부의 콘텐츠를 흡수합니다 — 이메일, PR 댓글, 문서, 소스 코드 등입니다.
  2. 해당 콘텐츠에는 지시사항이 숨겨져 있으며, 종종 인간 검토자에게는 말 그대로 보이지 않습니다 (CamoLeak은 GitHub의 보이지 않는 주석 구문을 사용했고, GitLab Duo 연구원들은 유니코드 스머글링 (Unicode Smuggling)과 KaTeX의 흰색 글자 (White-on-white text) 기법을 사용했습니다).
  3. AI는 자신의 정당한 권한을 사용하여 해당 지시사항을 수행합니다.
  4. AI는 자신이 볼 수 있도록 허용된 비밀 정보를 가져와 출력값에 포함시킵니다.
  5. 사용자는 아무런 잘못도 하지 않았습니다. 종종 사용자는 완전히 일상적인 질문을 던졌을 뿐입니다.

이것은 전형적인 혼란스러운 대리인 (Confused Deputy) 사례입니다. AI가 대리인(deputy)이며, 실제 권한을 가지고 있고, 비밀 정보에 직접 접근할 수 없는 공격자가 대리인을 속여 해당 정보를 가져오게 만듭니다. 참신함은 '혼란스러운 대리인'이라는 아이디어 자체에 있는 것이 아닙니다. 이 개념은 수십 년이나 되었습니다. 진짜 참신한 점은 명령(instruction)이 콘텐츠 내에 숨겨진 자연어 (Natural Language) 형태라는 것이며, 이로 인해 기존의 일반적인 방어 기제들이 전혀 작동하지 않는다는 점입니다. 입력 유효성 검사 (Input Validation) 단계에서 거부할 만한 잘못된 형식의 페이로드 (Malformed Payload)가 없습니다. 또한 "이 단락은 명령이 아니라 데이터이다"라고 명시하는 스키마 (Schema)도 없습니다. 모델은 컨텍스트 윈도우 (Context Window) 내의 모든 것을 실행 가능한 것으로 간주하며, 컨텍스트 윈도우는 신뢰할 수 있는 콘텐츠와 신뢰할 수 없는 콘텐츠를 동일한 평면에서 혼합합니다.

이것이 방어하기 진정으로 어려운 이유
솔직히 말하자면, 자연어를 완전히 스키마 검증 (Schema-validate)할 수는 없으며, 모델이 검색된 콘텐츠를 명령으로 취급하는 것을 쉽게 막을 수도 없습니다. 왜냐하면 검색된 콘텐츠를 의미 있는 것으로 취급하는 것 자체가 어시스턴트의 존재 목적이기 때문입니다. 위에 언급된 벤더들은 부실한 코드를 배포한 것이 아닙니다. 그들은 유용한 기능을 배포했으며, 그 유용성이 바로 공격 표면 (Attack Surface)이 된 것입니다. Microsoft는 서버 측 (Server-side) 패치를 진행했고, GitHub은 Copilot Chat에서 이미지 렌더링을 비활성화했으며, GitLab은 외부 도메인으로의 안전하지 않은 HTML 렌더링을 차단했습니다. 이 모든 것은 사후 대응적이었으며, 모두 취약점 공개 이후에 이루어졌습니다.
(저는 데이터 유출 채널 — 이미지 URL 트릭, 프록시 오용, CSP 우회 등 — 에 대해 의도적으로 자세히 설명하지 않겠습니다. 그것들은 실제 존재하는 방식이지만 웹 렌더링 계층 (Web-rendering layer)의 문제이며, 개발자들이 일반화하여 배울 수 있는 부분이 아닙니다. 일반화할 수 있는 부분은 상류 (Upstream) 단계에 있습니다. 즉, 모델이 절대 출력해서는 안 되는 비밀 정보가 출력값에 나타났다는 사실입니다.)

이것이 제가 만들고 있는 것과 연결되는 지점
이 패턴은 바로 제가 앱의 안전 여부를 LLM에게 판단하도록 요청하는 대신, rojaprove를 통해 결정론적 (Deterministic)인 방식을 택한 이유입니다.
만약 실패의 정의가 "절대 나타나서는 안 될 비밀 정보가 나타났다"라면, 판단 (Judgment call)이 필요한 것이 아니라 카나리 (Canary)가 필요합니다. 응답에 나타날 정당한 이유가 전혀 없는 문자열을 시스템 프롬프트 (System Prompt)에 심어두세요. 그리고 이러한 공격들이 사용하는 종류의 입력값들을 보내보십시오.

그다음, 카나리 (canary)가 다시 출력되었는지 정확한 문자열 일치 (exact string match) 방식으로 확인하십시오. 결과는 나왔거나, 나오지 않았을 것입니다. 다른 모델이 다른 모델의 출력을 해석하는 과정도, 확률도 개입되지 않습니다.

범위(scope)에 대해 정확히 말씀드리고자 합니다. 이는 매우 중요하기 때문입니다. 현재 rojaprove는 시스템 프롬프트 유출 (system-prompt leakage, OWASP LLM07) — 즉, 유출된 대상이 프롬프트 그 자체인 영역을 탐지합니다. EchoLeak/CamoLeak/Duo에서 보여준 '간접 주입에서 유출로 이어지는 전체 체인 (full indirect-injection-to-exfil chain)'은 현재 제가 탐지하는 범위보다 더 넓습니다. 해당 기능들은 현재 제 로드맵에 포함되어 있으며, 아직 "테스트 완료" 항목에 있지는 않습니다. 또한, 제가 의도적으로 다루지 않는 인접한 클래스가 있는데, 바로 멀티 테넌트 접근 제어 (multi-tenant access control, "사용자 A가 사용자 B의 행을 읽을 수 있는가")입니다. 이 경우에는 카나리를 사용할 수 없기 때문입니다. 두 레코드 모두 실제 데이터이며, 절대 나타나서는 안 될 것이 존재하지 않으므로, 유출 형태의 체크 방식으로는 이를 포착할 수 없습니다. 저는 광범위하고 모호하게 접근하기보다, 좁더라도 결정론적 (deterministic)으로 접근하는 쪽을 택하겠습니다.

하지만 GitLab Duo의 사례는 왜 카나리 방식이 효과적인지를 보여주는 가장 명확한 예시입니다. 연구자들은 base16 인코딩, 유니코드 밀수 (Unicode smuggling), KaTeX 흰색 텍스트 등을 사용하여 프롬프트를 숨겼습니다. 이러한 모든 은닉 기법은 인간과 대부분의 필터에는 보이지 않지만, 심어둔 카나리는 지시 사항이 어떻게 숨겨졌는지에는 신경 쓰지 않습니다. 카나리는 오직 비밀 정보가 표면으로 드러났는지 여부만을 확인합니다. 이것이 바로 그라운드 트루스 오라클 (ground-truth oracle)이 가진 매력의 전부입니다. 상류 (upstream)에서의 공격자의 교묘함이 하류 (downstream)에서의 예/아니오(yes/no) 결과를 바꾸지는 못합니다.

이 프로젝트는 BSL 1.1 라이선스의 무료 오픈 소스입니다.
→ github.com/ghkfuddl1327-wq/rojaprove

만약 LLM 애플리케이션을 출시하신다면: 출시 전에 이러한 형태의 공격을 테스트해 보셨습니까, 아니면 사고 보고서를 통해서나 알게 될 종류의 문제입니까? 그리고 체인의 어느 부분 (프롬프트 유출, 간접 주입, 유출)을 먼저 포착하는 것이 실제로 유용할까요? 진심으로 여쭤봅니다. 저는 사람들이 정말로 필요로 하는 영역을 구축하고 싶습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0