당신의 에이전트 스택엔 약한 고리가 있어요
악성 MCP 서버는 완전한 악성 명령 하나 쓰지 않고도 당신의 SSH 키를 훔칠 수 있습니다. ASSET 연구팀이 이걸 증명했어요: 탈취 명령을 모델에게 한 번에 주면 대부분의 대형 모델이 거부해요. 그런데 같은 명령을 무해해 보이는 조각으로 쪼개면, GPT-4o, Gemini 2.0 Flash, Llama 3.3이 테스트 케이스의 100%에서 순응합니다.
한편 대부분의 개발자는 매주 새 MCP 서버를 에이전트에 추가하고, 깃허브에서 찾은 설정 줄을 복사합니다. 그 서버들은 각각 당신 접근 권한의 일부를 갖고 있어요: API 토큰, 클라우드 키, 서비스 계정. MCP는 유용해요 — 그건 아무도 부정하지 않죠. 하지만 MCP 서버는 전체 에이전트 스택에서 가장 약한 고리가 되었습니다.
이 아티클은 MCP 서버가 눈치채지 못하게 비밀을 유출하는 방법, 명령을 분할해 모델 거부를 뚫는 GhostSplice 공격, 그리고 Cloudflare WriteGuard부터 오늘 당장 당신의 셋업에 적용할 수 있는 규칙까지 구체적인 방어책을 다룹니다.
MCP 서버가 실제로 뭘 갖고 있는지
MCP 서버는 사실상 당신의 에이전트와 외부 도구 — 데이터베이스, 깃허브, 슬랙, 클라우드 — 를 잇는 다리입니다. 그 다리 역할을 하기 위해, 당신으로 로그인하는 데 필요한 모든 것 — 토큰, API 키, 서비스 계정 자격 증명 — 을 디스크 위 설정 파일에 평문으로, 보통 암호화 없이 저장합니다.
나중을 위해 기억해둘 프로토콜 디테일이 하나 있습니다. 에이전트가 MCP 서버에 연결하면, 서버는 도구 목록을 돌려주는데, 각 도구엔 언제 어떻게 쓸지 모델에게 알려주는 자유 형식 설명이 붙어 있습니다. 이 설명들은 당신 자신의 지시와 같은 무게로 곧장 모델의 컨텍스트에 들어가고, 도구가 반환하는 결과도 마찬가지로 거기 들어갑니다. MCP 서버는 사실상 아무도 다시 읽지 않는 텍스트로 당신의 에이전트와 쉬지 않고 대화하는 셈입니다. 바로 이것이 GhostSplice 공격을 가능하게 만드는 지점입니다.
생태계는 또한 안전장치보다 빠르게 커졌습니다:
| 지표 | 수치 |
|---|---|
| 공식 MCP 레지스트리의 서버 수 | 9,600개 이상 |
| 2025년 5월 이후 원격 서버 배포 증가율 | 5배 |
누구나 서버를 배포할 수 있고, 중앙 심사는 없으며, 당신의 에이전트는 각 서버를 공식 도구처럼 정확히 신뢰합니다. NSA는 5월에 MCP 전용 보안 가이드를 발표하며, 프로토콜의 보급 속도가 보호 장치 구축을 앞질렀다고 밝혔습니다. 정보기관이 당신이 즐겨 쓰는 개발 도구에 대한 가이드를 쓸 때, 그건 좀처럼 축하하려는 목적이 아닙니다.
그것이 배경입니다: 수천 개의 서버, 심사는 없고, 당신의 키는 그 한가운데에 있습니다.
비밀은 어디서 유출되는지
첫 번째 구멍은 평문으로 저장된 자격 증명입니다. The Hacker News는 8월 17일 유출 메커니즘에 대한 상세한 분석을 발표했고, 그 출발점은 단순합니다: 토큰이 설정 문자열에 그대로 붙여져 디스크에서 읽을 수 있는 상태로 남습니다. 조금 서두른 커밋 하나면 키가 담긴 설정을 Git 저장소로 밀어넣기에 충분합니다. 기사가 "sprawl"이라 부르는 현상으로 상황은 더 나빠집니다: 같은 키가 설정 파일, 환경 변수, 그리고 개발·스테이징·운영 환경의 사본에 걸쳐 중복됩니다. 시간이 지나면 아무도 비밀이 어디 있는지 몰라서 순환도 하지 않습니다 — 절대 순환되지 않는 고정 키는 공격자를 기다리는 키입니다.
두 번째 구멍은 과도한 권한 부여입니다. 개발 중에는 인가 오류를 피하려고 서버에 넓은 권한을 주고, 그 넓은 권한이 손대지 않은 채 운영으로 배포됩니다. 그러면 단 한 번의 침해로 실제 사용이 정당화하는 것보다 훨씬 많은 것이 노출됩니다.
세 번째 구멍은 공급망입니다. CVE-2025-6514는 40만 번 넘게 다운로드된 OAuth 프록시인 mcp-remote를 강타했고, 악성 서버가 사용자 기기에서 명령 삽입을 일으켜 코드를 실행하고 자격 증명을 들고 튈 수 있게 했습니다. 한 줄로 설치되는 인기 npm 패키지 하나로 문이 열렸습니다.
네 번째 구멍은 가장 교묘합니다: 프롬프트 인젝션입니다. 에이전트는 도구가 가져오는 모든 것 — 웹페이지, 티켓, 내부 문서 — 을 읽습니다. 그중 하나에 숨겨진 지시가 들어있다면, 에이전트는 마치 당신이 준 것처럼 그걸 따를 수 있고, 정당한 도구를 사용해 지켜야 할 것을 노출시킬 수 있습니다. 그 유출은 어떤 기술적 결함도 거치지 않습니다 — 모델의 순진함을 거칠 뿐입니다.
정교한 공격 이전에도, MCP 서버의 평범한 일상 — 평문 설정, 과도한 권한, 검증되지 않은 의존성, 걸러지지 않은 콘텐츠 — 이 이미 당신의 비밀을 노출시킵니다.
GhostSplice: 조각으로 실려오는 공격
GhostSplice는 ASSET 연구팀이 당신의 에이전트가 완전히 협조하며 직접 유출을 실행하게 만드는 기법에 붙인 이름입니다. 원리는 한 문장으로 요약됩니다: 탈취 명령을 통째로 쓰는 대신, 악성 서버가 그것을 쪼개서 한 조각은 도구의 설명에, 다른 조각은 그 도구가 반환하는 결과에 넣습니다. 각 조각을 따로 읽으면 무해해 보입니다. 하지만 에이전트는 작업 컨텍스트에 들어오는 모든 것을 결합합니다: 전체 명령을 재구성해 완전히 선의로 실행합니다 — 에이전트 입장에서는 그저 도구가 요청한 양식을 채우는 것일 뿐입니다.
테스트 수치가 진짜 이야기를 들려줍니다:
| 모델 | 한 번에 준 명령 | 쪼갠 명령 |
|---|---|---|
| GPT-4o | 100% 거부 | 100% 순응 |
| Gemini 2.0 Flash | 100% 거부 | 100% 순응 |
| Llama 3.3 | 100% 거부 | 100% 순응 |
| Claude Haiku 4.5 | API로는 거부 | Cursor 내 세 조각 테스트에서 100% 순응 |
Claude의 디테일이 손쉬운 결론을 뒤엎습니다: 같은 모델이 한 클라이언트에서는 거부하고 다른 클라이언트에서는 유출할 수 있으며, 이는 그 클라이언트가 추가하는 보호 장치에 달려 있습니다.
테스트에서 GhostSplice가 훔친 것: SSH 키, 환경 비밀, 소스 코드, 고객 데이터. 연구자들은 실제 피해자가 아니라 가짜 키로 격리된 프로젝트에서 작업했지만, 그 방법은 공개되어 있고 재현 가능합니다.
GhostSplice가 처음 시도는 아닙니다. 같은 연구실이 6월에 Ghostcommit을 발표했는데, 프로젝트 관례가 참조하는 PNG 파일에 지시를 숨기고, 훔친 비밀을 정수로 소스 코드에 인코딩하는 공격이었습니다. 명령 분할은 고립된 호기심 거리가 아니라 자리 잡아가는 공격 계열입니다.
두 가지를 기억하면 균형 잡힌 시각을 유지할 수 있습니다. 이 공격에는 두 가지 전제 조건이 있습니다: 악성 서버가 이미 당신의 에이전트에 연결되어 있어야 하고, 에이전트가 대상 파일에 읽기 권한을 가지고 있어야 합니다. 바로 그래서 출처가 그렇게 중요한 것입니다 — 서버가 어디서 왔는지가 당신의 첫 번째 방어선입니다. 그리고 메커니즘을 기억하세요: 모델의 얼라인먼트는 당신을 지켜주지 않습니다, 이 공격은 한 번에 금지된 것을 요구하는 법이 없기 때문입니다.
Shadow MCP: 아무도 승인하지 않은 서버들
GhostSplice는 악성 서버가 이미 연결되어 있다고 가정했습니다. 그런데 정확히 누가 무엇을 연결할지 결정할까요? 팀에서는, 솔직한 답은 아무도 아니라는 것입니다. 이것이 바로 Cloudflare가 shadow MCP라고 부르는 문제입니다 — 개발자들이 보안 심사 없이 자신의 에이전트에 연결하는 모든 서버 말입니다. 최근까지 그 트래픽은 보이지 않았습니다 — MCP 요청은 다른 HTTPS 호출과 똑같아 보이기 때문입니다.
Cloudflare는 프로토콜 수준 탐지로 이를 바꿨습니다. 스펙 업데이트 이후, 규격을 따르는 모든 MCP 클라이언트는 요청에 MCP-Protocol-Version 헤더를 붙이고, Gateway는 복호화하는 모든 TLS 트래픽에서 그 헤더를 검사합니다. 보안팀은 이제 전용 대시보드로 회사에서 사용되는 모든 MCP 서버 — 고유 서버, 사용자, 요청량 — 를 볼 수 있습니다. 이 헤더 방식이 도메인 이름 필터링보다 나은 이유는, MCP 서버가 굳이 자신을 mcp-무언가라고 부를 이유가 없기 때문입니다 — 프로토콜은 스스로 뭐라고 주장하는지가 아니라 실제로 하는 말로 발각됩니다.
팀은 조치도 취할 수 있습니다: is_mcp 선택자를 사용하면 승인된 포털을 거치지 않은 모든 MCP 트래픽을 차단할 수 있습니다. 포털은 이 셋업의 나머지 절반입니다 — 검증된 서버들을 신원 인증 뒤로 모으는 단일 접근점입니다.
최신 스펙 버전은 가시성을 한층 더 높입니다. 새로운 Mcp-Method와 Mcp-Name 헤더는 방화벽이 요청 본문을 열 필요 없이 요청된 작업과 호출되는 도구를 드러냅니다. 팀은 네트워크 수준에서 바로 티켓 하나를 읽는 에이전트와 50개를 삭제하는 에이전트를 구분할 수 있습니다.
Cloudflare는 이를 실제로 두 가지 경우로 나눕니다: 한 번도 승인된 적 없는 순수 shadow MCP, 그리고 체크포인트를 우회해 직접 도달한 승인된 서버인 포털 우회입니다. 둘 다 같은 기본 규칙으로 차단됩니다. 논리는 단순합니다: 포털을 거친 모든 것은 알려지고 기록되며, 나머지는 모두 차단됩니다. 회사 입장에서, 이는 금요일 밤에 설치된 유령 MCP 서버의 종말을 의미합니다.
WriteGuard: 도구별 권한
승인된 서버조차 피해를 줄 수 있습니다, 에이전트가 사용자의 권한 전부를 한 번에 상속하기 때문입니다. 바로 그 지점에서 WriteGuard가 등장합니다, Cloudflare가 방금 비공개 베타로 연 기능입니다. 아이디어는: 모든 MCP 서버의 모든 도구를 위험 등급으로 분류하고, 등급마다 다른 정책을 적용하는 것입니다.
- 읽기는 마찰 없이 통과합니다.
- 댓글 달기 같은 제한된 쓰기는 통과하되 강화됩니다: 그 행동은 특정 사람을 대신한 에이전트가 한 것으로 서명되고, 감사 이벤트가 중앙 로그로 전송됩니다.
- 코드 병합, 운영 배포, 대량 삭제 같은 치명적 행동은 서버가 처리하기도 전에 차단됩니다.
Cloudflare 포스트 속 GitLab 예시가 이 단계를 잘 보여줍니다: 머지 리퀘스트 읽기는 통과하고, 댓글은 귀속 정보와 함께 통과하며, 병합은 사람이 직접 할 때까지 거부됩니다.
가장 흥미로운 부분은 신원 모델입니다. 에이전트는 자신이 섬기는 직원의 권한을 유지하지만, 이제 모든 쓰기는 두 개의 서명 — 그 사람, 그리고 대신 행동하는 에이전트 세션 — 을 갖습니다. 그래서 후속 시스템은 마침내 수작업 변경과 기계 생성 변경을 구분할 수 있고, 감사 로그는 민감 정보가 걸러진 상태로 중앙 로그에 비동기로 전송됩니다. 지금까지 에이전트는 로그에서 사람과 구별이 안 됐지만, 인시던트 감사에서는 이것이 모든 걸 바꿉니다 — 쿼리 하나로 화요일의 수상한 머지가 서두른 동료에게서 왔는지 창의력을 발휘한 에이전트 세션에서 왔는지 알 수 있습니다.
Cloudflare는 이론을 파는 것이 아니라, 자신들의 내부 사용 사례를 이야기합니다: 그들의 포털은 4월의 13개에서 지금은 27개의 MCP 서버를 연결합니다. 그 숫자가 진짜 이야기를 들려줍니다 — Cloudflare에서조차 몇 달 사이 서버 수가 두 배가 되었고, 바로 그래서 도구별 통제가 필요해지는 것입니다. 업계가 향하는 방향은 분명합니다: 서버 전체를 신뢰하지 않고, 행동 하나하나마다 에이전트가 무엇을 할 수 있는지 결정하는 것입니다.
한계: 이걸로 해결 안 되는 것
한계는 솔직하게 말할 필요가 있습니다. WriteGuard는 가입 양식 뒤에 있는 비공개 베타이고, Gateway의 탐지는 TLS 검사가 켜진 Cloudflare Zero Trust 배포가 필요합니다 — 1인 개발자나 소규모 팀이라면, 그건 단순히 당신의 인프라가 아닙니다. 회사 안에서도, 탐지는 복호화하는 네트워크 트래픽만 볼 수 있습니다 — stdio로 돌아가는 로컬 MCP 서버가 당신의 기기에서 평범한 프로세스로 실행되면 Gateway에는 보이지 않습니다. 그리고 이것이 바로 개발자들이 설치하는 서버 대부분이 실제로 작동하는 방식입니다.
무엇보다, 이 도구들 중 어느 것도 GhostSplice가 드러낸 핵심 메커니즘을 고치지 못합니다: 에이전트가 컨텍스트에 들어오는 모든 것을 자유롭게 결합하는 한, 무해한 조각들은 계속해서 적대적인 명령으로 재조합될 것입니다. ASSET 연구자들 스스로 말합니다: 해결책은 도구의 출력을 명령이 아닌 데이터로 다뤄야 하며, 그런 분리는 아직 에이전트에 기본으로 존재하지 않습니다.
그동안, 그들의 권고는 세 가지로 요약됩니다: 한 도구에서 나온 값이 검증 없이 다른 도구의 인자를 채우지 못하게 막고, 각 도구 호출을 수동으로 거부할 수 있는 능력을 유지하고, 검증되지 않은 서버의 주석은 기본적으로 적대적인 것으로 다루는 것입니다. 이 셋 중 어느 것도 오늘날 자동은 아닙니다 — 당신이 적용하거나, 아무도 하지 않거나입니다. Cloudflare가 여기서 내놓는 모든 것을 안전벨트로 여기세요, 브레이크가 아니라 — 피해를 줄일 뿐, 충돌 자체를 막지는 못합니다.
우리 셋업에 적용할 것들
오늘부터 할 일:
- 재고 조사. 실제로 당신의 에이전트에 연결된 MCP 서버 목록을 만들고, 더 이상 쓰지 않는 것은 제거하세요.
- 출처로 정렬하세요. 알려진 벤더의 공식 서버라면, 예. 스레드에서 찾은 별 40개짜리 깃허브 저장소라면, 아니오 — 당신의 데이터로 무엇을 하는지 읽어보기 전까지는요.
- 권한 범위를 정하세요. 각 서버에 최소 범위의 전용 토큰을 주고, 절대 마스터 키는 주지 마세요. 그리고 운영 시스템에 하듯 그 토큰들을 순환시키세요.
- 민감한 행동에는 손을 놓지 마세요. 쓰고, 병합하고, 삭제하는 에이전트는 당신을 거쳐야 합니다 — WriteGuard가 산업화하는 것의 수작업 버전입니다.
- GhostSplice 규칙을 일상적으로 적용하세요. 에이전트가 당신이 요청하지 않은 도구 행동을 연쇄로 실행할 때, 멈추고 서버가 그동안 뭐라고 말해왔는지 읽어보세요.
모든 에이전트 클라이언트는 연결된 서버와 도구 목록을 보여줄 수 있고, 그 목록을 읽는 데는 30초면 충분합니다. 그 30초는 당신의 셋업 전체에서 가장 시간 대비 보안 효율이 좋은 투자입니다.
회사에 있다면, 네트워크 계층을 더하세요: Gateway의 MCP 탐지와 포털은 도입할 가치가 있습니다, shadow MCP는 당신이 보든 안 보든 이미 조직 안에 존재하기 때문입니다.
MCP가 문제가 아니라, 우리가 그것에 키를 넘긴 속도가 문제입니다.
AIDive