일은 열세 개의 에이전트로 나눴는데, Paperclip은 키를 나누지 않았어요.
스타 71,000개를 받은 에이전트 프레임워크를 스캔한 결과, 열세 개 에이전트 중 열두 개가 동일한 평문 토큰을 들고 있었어요. 에이전트를 여러 대 운영하기 시작하면 '비밀이 설정 파일에 산다'는 관행은 더 이상 지름길이 아니라 증폭기가 돼요.
요즘 방식대로 하셨네요. 큰 에이전트 하나 대신 에이전트를 여러 대 띄웠어요 — 티켓을 분류하는 에이전트, 카피를 쓰는 에이전트, 디자인 파이프라인을 돌리는 에이전트, 열두 대쯤 되는 각각의 에이전트가 자기 일과 자기 설정을 갖고 있어요. 그렇게 하면 더 안전해 보여요. 파급 범위가 작고, 최소 권한에 더 가깝거든요.
그런데 보안 스캔이 스타 71,000개를 받은 Paperclip이라는 에이전트 프레임워크의 설정 파일들을 훑다가, 열세 개 에이전트 중 열두 개가 동일한 자격 증명을 들고 있다는 걸 발견했어요 — 똑같은 토큰이 각 에이전트의 설정에 평문으로 붙여넣어져 있었죠 [1]. Anthropic API 키 하나는 디자인 에이전트 설정에 평문으로 하드코딩되어 있었어요. 한 에이전트용으로 만든 봇 토큰은 상관없는 에이전트도 읽을 수 있었고요.
문이 열세 개. 키는 하나, 그중 열두 개에 복사되어 있어요. 가장 약한 에이전트에서 훔치면 나머지 열한 개도 손에 넣어요.
실제로 무슨 일이 있었나
Paperclip은 각 에이전트에 MCP 서버 설정을 줘요 — 어떤 도구와 서비스에 접근할 수 있고, 거기에 어떻게 인증하는지를 알려주는 파일이에요. 어딘가에서 비밀이 그 파일에 문자 그대로 들어가 버렸어요: n8n JWT, bearer 토큰, Anthropic API 키. 참조한 게 아니라, 런타임에 주입된 것도 아니라, 직접 입력한 거예요. 그리고 다음 에이전트를 띄우는 일이 복사-붙여넣기다 보니, 그대로 전 에이전트에 복제됐죠.
스캔은 세 가지를 지적했어요. 열두 개 에이전트에 공유된 평문 토큰(HIGH 등급), 디자인 에이전트 설정에 평문으로 놓인 Anthropic 키(CRITICAL 등급), 그리고 애초에 대상이 아니었던 에이전트에 부여된 봇 토큰 — 전형적인 최소 권한 위반 [1]. 공정하게 말하면 Paperclip 팀은 빠르게 움직였어요. 자격 증명 참조로 옮기고, 에이전트 간 읽기 시 설정을 마스킹하고, 바인딩 동기화를 강제하기 시작했죠 [2]. 올바른 방향이에요.
이건 Paperclip이 부주의했던 게 아니에요
곰곰이 생각해볼 부분이 있어요. Paperclip은 거의 모든 프레임워크가 하는 일을 했을 뿐이에요. 비밀을 설정 파일에 넣는 건 30년 동안 소프트웨어가 인증해온 방식이에요. 앱이 하나, 설정이 하나, 키가 어디 있는지 아는 운영자 한 명이었기 때문에 통했어요.
그 습관 아래에서 시대가 바뀌었어요. 멀티 에이전트 시스템은 설정 하나짜리 앱 하나가 아니에요 — 프로세스 열두 개, 각각 파일 하나씩, 각각 앞 것을 복사한 버전이죠. 설정에 평문을 넣는 건 새어나갈 곳이 하나일 때 용인할 만한 지름길이었어요. 열세 개에서는 같은 지름길이 곧 유출 하나가 유출 열세 개라는 뜻이에요 — 그리고 '어느 에이전트가 그랬지?'에 답이 없어요. 로그에 남은 토큰이 전부의 것이었으니까요.
Paperclip이 내놓은 수정안인 자격 증명 참조는 확실히 낫지만, 무엇이 바뀌고 무엇이 안 바뀌는지 봐야 해요. 참조는 여전히 에이전트가 실행되는 곳에서 실제 비밀로 해석돼요. 에이전트 자신이든, 그것을 침해한 무엇이든 해석된 값을 읽을 수 있죠. 그리고 프레임워크 자체 버그 트래커에는 이미 다음 실패 모드가 올라와 있어요: 참조가 바인딩과 어긋나면서 설정은 채워진 것처럼 보이는데 검증이 조용히 실패하는 경우 [3]. 비밀이 한 층 뒤로 갔을 뿐, 건물을 떠나지 않았어요.
Paperclip만의 일이 아니에요
같은 주, 같은 근본 원인, 다른 저장소. 널리 쓰이는 코딩 에이전트가 .env의 원시 값 — 비밀번호, 토큰, API 키 — 을 채팅 출력에 그대로 찍어내는 것으로 이슈가 올라왔어요. 또 다른 에이전트 러너는 전체 상위 환경을 하위 프로세스에 넘겨서, 모든 공급자 키가 자식 프로세스에 보이는 상태였죠 [4]. 음성 훅은 대화 내용 전체를 자격 증명과 함께 전 세계에서 읽을 수 있는 /tmp에 쓰고 있었어요 [5]. 독립된 팀, 독립된 위협 모델, 공유된 가정 하나: 비밀이 에이전트가 볼 수 있는 곳에 살아도 괜찮다는 것. 공격자가 해야 할 주장은 전부 '그렇지 않다'는 하나예요.
일부러 이렇게 만들었어요
Clavitor는 정반대의 가정에서 출발해요: 에이전트가 자격 증명을 애초에 들고 있지 않다는 거예요. 에이전트는 동작을 요청하고, 그 요청은 가로채져서 에이전트가 읽을 수 없는 비밀로 인증된 뒤 처리돼요. 토큰을 붙여넣을 설정 파일이 없어요. 설정에 토큰이 없으니까요. 열세 개 에이전트에 복사할 것도 없어요. 에이전트 환경에 훔칠 만한 것이 애초에 없으니까요.
각 에이전트는 지정된 대상에만 접근해요 — 저장소 전체가 아니라 — 그래서 봇 토큰이 요청한 적 없는 에이전트에게 읽히는 일이 없어요. 그리고 모든 동작은 그 동작을 실행한 특정 주체에 기록돼요. 열두 개 에이전트가 공유하던 토큰에 기록되는 게 아니라 — 그래서 '누가 그랬지?'에 답이 있어요.
솔직하게 말하면, 이건 에이전트를 해킹 불가능하게 만들지 않아요. 침해된 에이전트는 여전히 그 순간에 권한을 받았던 동작을 할 수 있어요. 할 수 없는 건 키를 들고 나가 다른 열두 개가 되는 일이에요 — 들고 나갈 키가 손에 없으니까요.
교훈은 '토큰을 교체하라'가 아니에요
Paperclip은 토큰을 교체하고, 마이그레이션을 끝내고, 이슈를 닫을 거예요. 좋아요 — 그래야 해요. 하지만 교체가 교훈은 아니에요. 교훈은 앱 하나 대신 에이전트 여러 대를 운영하는 순간, '비밀이 설정 파일에 산다'는 것이 지름길이 아니라 증폭기가 된다는 점이에요. 증폭기는 비밀을 조금 더 읽기 어렵게 만든다고 고쳐지지 않아요. 비밀이 애초에 에이전트 손에 없도록 만드는 것으로 고쳐져요.
에이전트 시대에 자격 증명 도구가 지켜야 한다고 저희가 보는 규칙들을 적어뒀어요 — 그중에는 비밀이 코드가 실행되는 곳에 살지 않을 것, 에이전트는 지정된 대상에만 접근할 것 같은 것들이 있어요. 여러분의 도구를 이 규칙에 비춰보세요: clavitor.ai/rules.
Clavitor (@clavitorai)는 AI 에이전트를 위해, 그리고 그들에 맞서 만들어진 자격 증명 금고예요. clavitor.ai
출처
[1] Paperclip 에이전트 프레임워크 — 자격 증명 위생 점검 결과 (CFG-H1 공유 평문 토큰, CFG-C1 하드코딩된 Anthropic 키, CFG-H2 잘못된 범위의 봇 토큰): https://github.com/paperclipai/paperclip
[2] Paperclip — 라이프사이클 전반에 걸쳐 에이전트 비밀 바인딩 동기화 강제 (병합됨): https://github.com/paperclipai/paperclip/pull/8307
[3] Paperclip — secret_ref 환경 항목이 secret_bindings 행과 어긋날 수 있음, 설정은 채워진 것처럼 보이지만 검증이 조용히 실패 (#8309): https://github.com/paperclipai/paperclip/issues/8309
[4] Chetter — runBatchAgent가 러너 환경 전체를 상속받아 공급자 API 키가 하위 프로세스에 노출됨 (#56): https://github.com/flatout-works/chetter/issues/56
[5] Claude Code 음성 훅 — 전체 대화 내용(자격 증명 포함)이 전 세계에서 읽을 수 있는 /tmp에 기록됨 (#58): https://github.com/rodlaneedu-hash/claude-code-voice-hook/issues/58