본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 02. 04:31

당신의 코딩 에이전트에게는 비밀 정보가 필요하지 않습니다

요약

코딩 에이전트 사용 시 발생할 수 있는 민감 정보 유출 문제를 해결하기 위해 'Redact(가리기)'와 'Rehydrate(다시 채우기)' 방식을 제안합니다. 로컬 프록시를 통해 비밀 정보를 플레이스홀더로 교체하여 모델에 전달함으로써 보안을 강화하는 기술적 접근법을 다룹니다.

핵심 포인트

  • 코딩 에이전트의 .env 파일 및 민감 정보 유출 위험성 경고
  • 로컬 프록시를 활용한 데이터 마스킹 및 복원 메커니즘 제안
  • 모델이 실제 비밀 정보를 보지 못하게 하는 보안 설계의 중요성
  • 프롬프트 주입을 통한 플레이스홀더의 일관된 처리 방식 설명

제가 사용하는 모든 코딩 에이전트(Coding Agent)는 저의 .env 파일을 읽을 수 있습니다. 이들 모두는 단 한 번의 프롬프트(Prompt)만으로 그 내용을 제가 제어할 수 없는 서버로 스트리밍할 수 있는 상태입니다. Claude Code가 출시된 날부터 해결책은 명확했습니다. 나갈 때는 가리고(Redact), 들어올 때는 다시 채우는(Rehydrate) 방식입니다. 하지만 1년이 지난 지금도, 어떤 주요 벤더(Vendor)도 이 기능을 마땅히 있어야 할 클라이언트(Client)에 구축하지 않았습니다.

이와 유사한 기능을 수행하는 제3자 프록시(Third-party proxies)들이 존재하긴 합니다. 하지만 에이전트 자체가 이 기능을 탑재하여 출시되지는 않습니다. 그 간극이 바로 핵심입니다. 이 보호 조치가 가장 의미 있는 곳에 정작 그 기능이 빠져 있습니다.

불편한 가정에서부터 시작해 봅시다. 여러분이 추론 엔드포인트(Inference endpoint)로 보내는 모든 것은 보안이 전혀 없다고 가정하는 것입니다. 모든 주요 제공업체는 이에 반대할 것이며, 서류상으로는 그들의 말이 맞을 것입니다. 하지만 지난 2년간 발생한 제공업체 측의 유출 사고들은 최악의 상황을 가정하는 것이 더 안전한 선택임을 저에게 확신시켜 주었습니다. 만약 여러분이 공개된 Slack 채널에 특정 값을 붙여넣지 않을 것이라면, 원격 모델(Remote model)에게도 그것을 넘겨서는 안 됩니다. 이는 두 가지 중첩된 범주를 포함합니다. API 토큰, 개인 키(Private keys), 비밀번호와 같은 비밀 정보(Secrets), 그리고 이름, 전화번호, 그리고 가끔은 두 성격이 동시에 나타나는 사회보장번호와 같은 개인 데이터(Personal data)입니다. 중간에 위치한 프록시는 이 모든 것을 스캔할 수 있습니다.

해결책의 형태

로컬 프록시(Local proxy)가 에이전트와 추론 엔드포인트 사이에 위치합니다. 아웃바운드(Outbound) 측면에서 프록시는 페이로드(Payload)를 스캔하여 민감해 보이는 모든 것을 찾아내고, 각 일치 항목을 결정론적 플레이스홀더(Deterministic placeholder)로 교체합니다. 예: [[REDACTED_PHONE:8f3a]], [[REDACTED_TOKEN:3f2a]]와 같이 고유한 값당 하나씩 생성합니다. 가려진 페이로드와 함께, 프록시는 모델에게 해당 플레이스홀더가 무엇인지 알려주는 짧은 지침을 추가합니다. 즉, 클라이언트가 사적으로 보유하고 있는 불투명한 문자열(Opaque strings)이며, 해독할 수 없는 식별자로 취급되어야 하고, 응답에서 필요할 경우 있는 그대로 재현되어야 한다는 내용입니다.

편집된 프롬프트(Redacted prompt)가 위로 올라갑니다. 모델은 실제 값을 한 번도 본 적이 없는 상태에서 작업을 수행합니다. 돌아오는 과정에서 프록시(Proxy)는 문자열 치환(String replace)을 통해 응답을 처리하며, 모든 플레이스홀더(Placeholder)를 원래의 값으로 교체합니다. 사용자는 정상적인 답변을 보게 됩니다. 모델은 의미 없는 토큰(Nonsense tokens)을 보았습니다. 비밀 정보는 기계를 떠난 적이 없습니다.

프롬프트 주입(Prompt-injection) 단계는 사람들이 건너뛰는 부분이지만, 바로 이 부분이 전체 시스템을 작동하게 만드는 핵심입니다. 모델이 [[REDACTED_PHONE:8f3a]]를 전화번호를 다루는 방식과 동일하게 취급하고 — 끝에 붙은 해시(Hash)와 함께 문자열 그대로를 변경 없이 반환하기만 한다면 — 재수화(Rehydration)는 사소한 조회(Lookup) 작업에 불과합니다. 단 하나의 플레이스홀더 형식만으로도 한 세션 내에서 무한한 수의 서로 다른 값을 대신할 수 있습니다.

만약 당신이 스택(Stack)을 제어할 수 있다면, 주입(Injection)은 전혀 필요하지 않을 것입니다. 벤더(Vendor)는 해당 매핑(Mapping)을 구조화된 사이드 채널(Side-channel) — JSON 첨부 파일이나 모델이 준수하도록 훈련된 필드 — 로 전달할 수 있으며, 프롬프트 계층보다 훨씬 낮은 곳에서 통과(Pass-through) 동작을 구현할 수 있습니다. 스택에 접근 권한이 없는 외부인으로서, 프롬프트 주입은 제가 가진 지렛대이며, 이 아이디어가 작동함을 증명하기에는 충분합니다. 이것은 실제로 출시되어야 할 버전은 아닙니다.

플레이스홀더-값 매핑(Placeholder-to-value map)은 메모리에 암호화된 상태로 존재합니다. 실제 코드에서 나타날 가능성이 낮은 구분자(Delimiter)를 선택하면 재수화(Rehydration) 과정에서의 충돌(Collision)은 0에 수렴합니다. 제가 단순함을 약간 과장하고 있는 것일 수도 있습니다. 몇 가지 예외 상황(Edge cases)이 존재하기 때문입니다. 모델이 정당하게 수정해야 하는 비밀 정보(드문 경우), 스트리밍 출력의 여러 청크(Chunk)에 걸쳐 있는 비밀 정보(번거로운 경우), 편집(Redaction) 과정에서의 오탐(False positives)(관리 가능한 수준). 이 중 어느 것도 연구 과제가 아닙니다. 이것들은 엔지니어링 작업(Engineering work)입니다.

왜 "그냥 Presidio를 사용하세요"로는 충분하지 않은가

Microsoft의 Presidio는 상당수의 국제적 형식과 경계선에 있는 흔치 않은 형식을 포함하여 개인정보(PII)를 잘 식별하고 비식별화(Redact)합니다. Yelp의 detect-secrets는 또 다른 명백한 구성 요소이지만, 이는 탐지기(Detector)일 뿐 비식별화 도구(Redactor)는 아닙니다. 이는 pre-commit baseline이 자격 증명(Credentials)을 차단할 수 있도록 이를 찾아낼 뿐, 전송 중인 데이터를 다시 작성하지는 않습니다. LiteLLM이나 Bifrost와 같은 프록시(Proxy)에 연결하면 탐지와 함께 외부로 나가는 데이터의 비식별화(Outbound redaction)를 얻을 수 있습니다.

하지만 여전히 얻을 수 없는 두 가지가 있습니다. 첫 번째는 깨끗한 재수화(Rehydration) 경로입니다. Presidio는 기술적으로 가역 모드(Reversible mode)를 가지고 있지만, 읽을 수 있는 플레이스홀더(Placeholder) 대신 AES로 암호화된 블롭(Blob)을 생성하며, 이를 되돌리는 것은 사용자가 직접 조율해야 하는 별도의 복호화 단계가 필요합니다. 원래 값을 다시 넣을 수 있게 해주는 매핑(Mapping)은 언어 모델(Language model)을 거치는 왕복 과정(Round trip)을 견디도록 설계된 적이 없으며, 탐지 전용인 detect-secrets는 이 부분에서 아무것도 제공하지 않습니다.

두 번째이자 아무도 언급하지 않는 것은 플레이스홀더가 무엇인지 모델에게 알려주는 프롬프트 인젝션(Prompt-injection) 레이어입니다. 이 레이어가 없으면 모델은 [REDACTED_TOKEN]을 쓰레기 데이터나 빈칸 채우기 연습 문제로 취급합니다. 깔끔한 통과(Pass-through) 대신 "REDACTED_TOKEN이 무엇을 가리키는지 잘 모르겠습니다"라는 답변을 받게 됩니다. 모델에게 이것이 플레이스홀더이며 그대로 두어야 한다는 것을 명시적으로 알려주어야 합니다. Presidio는 이를 대신 해주지 않으며, detect-secrets 역시 마찬가지입니다.

LiteLLM과 Bifrost 모두 통합 코드를 직접 작성할 의사가 있다면 이 모든 과정을 수동으로 스크립트화할 수 있게 해줍니다. 하지만 대부분의 개발자는 그렇게 하지 않을 것이며, 더 중요한 점은 대부분의 개발자는 로컬 추론 프록시 (local inference proxy)를 전혀 실행하지 않는다는 것입니다. 프록시를 구축하는 것은 고통스러운 일이며, 이를 계속 작동하게 유지하는 것은 더 최악입니다. 몇 주마다 업스트림 API (upstream APIs)가 변경되어, 저는 이 연결 부위를 유지하기 위해 심 (shim)을 계속 수정해야 합니다. 저는 그것이 상관없습니다. 하지만 Cowork 같은 도구를 통해 Claude Code를 사용하는 사람은 그럴 시간도, 의지도 없습니다. 고급 사용자만이 구축할 수 있는 보호책은 보호책이 아닙니다. 이것이 제 프로젝트의 솔직한 한계이기도 합니다. 다른 사람의 스택에 덧붙여진 프록시는 항상 연결 부위의 결함(seams)을 가질 수밖에 없습니다. 지속 가능한 버전은 도구 내부에 존재해야 합니다.

이것이 코딩 에이전트 내부에 있어야 하는 이유

코딩 에이전트(Coding agents)의 경우, 이 문제는 부수적인 것이 아니라 구조적인 문제입니다. .env 파일의 전체 목적은 애플리케이션에 필요하지만 개발자가 어디에도 붙여넣어서는 안 되는 값들을 보관하는 것입니다. 프로젝트 파일을 읽는 에이전트는 .env를 읽습니다. 새로운 코드를 작성하는 에이전트는 그 안에 있는 내용을 참조합니다. 에이전트의 역할과 비밀 정보의 목적은 설계상 서로 충돌 관계에 있습니다.

샌드박싱 (Sandboxing)은 진정으로 개선되었습니다. 자동 모드 (Auto mode)와 더 엄격해진 기본 권한 설정 덕분에 Claude Code는 9개월 전보다 훨씬 적게 대본을 벗어납니다. 하지만 이것들은 증상에 대한 임시방편(shims)일 뿐입니다. 아무리 영리한 프롬프트 엔지니어링 (Prompt engineering)이라 할지라도 비밀 정보가 절대 읽히거나 전송되지 않는다고 보장할 수는 없습니다. 그리고 숙련된 개발자가 하루에 생성하는 추론 요청 (inference requests)의 양을 고려할 때, 1%의 실수율은 더 이상 꼬리 위험 (tail risk)이 아니라 결국 모든 이에게 닥칠 거의 확실한 사건이 됩니다. Redact-rehydrate (삭제 후 재수화) 방식은 이 분야에서 울타리를 치는 것이 아니라 근본적인 해결책을 제시하는 첫 번째 방식입니다.

모든 주요 벤더(Vendor)들은 그 격차가 존재한다는 사실을 알고 있습니다. 그럼에도 격차가 여전히 남아 있는 이유는 평소와 같습니다. 작업이 까다롭고, 다음 데모보다 우선순위가 낮으며, 사용자들이 아직 충분히 강력하게 불만을 제기하지 않았기 때문입니다. 이러한 이유들은 현실적이지만, 그 어떤 것도 정당화될 수는 없습니다. 그리고 이것은 실행하는 것 자체가 어려운 것이 아니라, 실행하기로 결정하는 것이 어려운 드문 사례입니다.

아무도 주장하지 않은 차별점

이 기능을 출시한 벤더는 경쟁사들이 결코 할 수 없는 말을 할 수 있습니다. "우리는 귀하의 비밀 정보나 개인 데이터를 절대 볼 수 없도록 적극적으로 엔지니어링하고 있습니다. 우리에게 도달하는 데이터는 보안을 강화하고, 대부분의 데이터는 애초에 도달하지 않도록 도구(Tooling)를 구축합니다." 여기에 누구나 감사(Audit)할 수 있는 코드 경로(Code path)를 뒷받침한다면, 그것은 모든 설정 페이지에 이미 붙어 있는 "우리는 귀하의 개인정보를 소중히 여깁니다"라는 식의 벽지 같은 문구가 아니라 진정한 주장(Claim)이 됩니다.

이것은 정확히 Anthropic의 영역에 속합니다. OpenAI, Google 및 나머지 기업들과의 차별점은 언제나 신뢰(Trust)였습니다. 개별 기업에 대해 어떻게 생각하든 간에, 현재 더 많은 사람들이 Google이나 Meta에 데이터를 맡길 때보다 덜 망설이며 Anthropic에 데이터를 맡깁니다. 그리고 실제로 검증할 수 있는 기능을 통해 그러한 명성을 공고히 하는 것은 제품 전략 측면에서 가장 명확한 선택(No-brainer)에 가깝습니다. 소스 코드에서 읽을 수 있는 신뢰는 믿음으로 가져야만 하는 신뢰보다 강력합니다.

내가 만들고 있는 것

저녁 시간과 주말을 이용해 저는 Presidio의 기능 중 제가 중요하게 생각하는 부분들—개체 탐지(Entity detection), 구조화된 플레이스홀더 생성(Structured placeholder generation), 결정론적 교체(Deterministic replacement), 인체공학적 암호화(Ergonomic encryption)—을 octarine이라는 Rust 라이브러리로 재구현하고 있습니다. 이 라이브러리는 Anthropic, OpenAI 또는 동일한 API를 사용하는 다른 서비스들 앞에서 삭제-주입-재수화(Redact-inject-rehydrate) 과정을 투명하게 실행하는 로컬 프록시(Local proxy)로 감싸져 있습니다. 이것은 오픈 소스입니다. 여러분은 모든 줄을 읽어볼 수 있습니다.

그 규모는 상당합니다. Rust 코드만 약 200,000줄에 달하며, 그중 절반 가까이가 테스트 코드입니다. 그중 약 9,000개의 테스트가 변경 사항이 있을 때마다 실행됩니다. 또한 이것은 제가 Rust로 구축한 첫 번째 실질적인 프로젝트이기도 합니다. 약 95%를 에이전트(Agent)가 작성했고, 저는 시간이 허락하는 대로 검토(Review)를 수행했습니다. 이것을 겸손을 가장한 자랑이나 고백으로 말씀드리는 것이 아닙니다. 이 프로젝트가 제 논문의 축소판이기 때문에 말씀드리는 것입니다. 저는 정보 삭제 파이프라인(Redaction pipeline)의 아키텍처(Architecture), 검증(Validation), 실패 모드(Failure modes) 등 어디에 무엇이 숨겨져 있는지 잘 알고 있으며, 에이전트는 제가 일일이 찾아봐야 했을 구문(Syntax)을 처리합니다. 이것은 세 번째 시도입니다. 첫 번째는 Python에서 실패했고, 두 번째는 잘못된 Rust 아키텍처 때문에 실패했습니다. 세 번째가 살아남은 이유는 핵심 로직을 깔끔한 라이브러리(Library)로 분리하고, 잘 다듬어진 패턴(Patterns)이 구조를 유지하도록 했기 때문입니다. 그 과정에서 저는 기술 부채(Tech debt)가 고착되기 전에 이를 포착할 수 있는 저만의 에이전트와 스킬(Skills)을 몇 가지 구축했습니다.

제 자체 테스트 결과, 이것은 작동합니다. 모델은 추가적인 자극 없이도 플레이스홀더 토큰(Placeholder tokens)을 불투명한 문자열(Opaque strings)로 처리하며, 왕복 시간(Round trip)은 인지하지 못할 정도로 충분히 빠르고, 패턴이 조정되면 오탐률(False-positive rate)은 허용 가능한 수준으로 떨어집니다. 저는 이것이 '정답'이라는 환상을 가지고 있지는 않습니다. 이것은 하나의 실험입니다. 고급 사용자들에게는 유용하며, Rust가 무엇을 할 수 있는지 배울 수 있는 좋은 방법이자, 어려운 부분들도 다룰 수 있다는 것을 보여주는 실존적 증거입니다. 진짜 정답이 나타난다면, 그것은 전체 스택(Stack)을 제어하며 그 어떤 프록시(Proxy)보다 더 잘 해낼 수 있는 누군가에 의해 구축될 것입니다.

Anthropic이 해야 할 일

이 기술을 출시할 위치에 있는 몇 안 되는 기업 중, 저는 Anthropic에 걸겠습니다. 팀 규모가 작아서가 아니라(작지도 않습니다), 이를 우선순위로 다룰 수 있는 문화와 역량을 갖추고 있기 때문입니다. 그들에게는 1년이라는 시간이 있었습니다. 제가 오늘 오후에 Claude Code를 설치하고, 그럴듯하게 들리는 요청 한 번으로 저의 .env 파일을 원격 엔드포인트(Remote endpoint)로 스트리밍할 수 있다는 점은 제가 메워지기를 바라는 격차이며, 기꺼이 메우는 데 도움을 주고 싶은 격차입니다.

그것은 큰 작업이 아닙니다. 자원을 갖춘 팀이라면 제가 해온 모든 것, 그리고 그 이상의 것을 더 잘 해낼 수 있으며, 단 몇 주면 충분할 것입니다. 저는 토큰 예산(token budget), Rust 학습 곡선(learning curve), 그리고 우연히 비는 저녁 시간이라는 제약 조건 속에서 작업하고 있지만, 그들에게는 그러한 제약이 전혀 없습니다. 누군가가 클라이언트(client)에 이를 배포하기 전까지, 코딩 에이전트(coding agents)를 중심으로 도구(tooling)를 구축하는 모든 사람은 전송 중인 비밀 정보(secrets-on-the-wire)를 사후 고려 사항이 아닌, 최우선 과제(first-class concern)로 다루어야 합니다. 해결책은 작습니다. 하지만 이를 구축하지 않고 방치했을 때의 비용은 결코 작지 않습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0