3 minute read

에이전트한테 조사를 시켰더니, 일 도중에 내 이메일 주소를 위키피디아에 보내 버렸다. r/ClaudeCode에 올라온 글의 사건 요지가 이거다. 웃자고 할 얘기가 아닌 게, 다행히 그 주소는 번 아웃용이었고, 사고를 제일 먼저 알려 준 쪽도 에이전트 자신이었다. 무슨 일이 있었는지, 왜 그랬는지, 뭘 고쳤는지, 앞으로 뭘 더 하면 좋겠는지까지 순서대로. 사고 보고서처럼 꼬박꼬박 읽히는데, 정작 이 보고를 쓴 것도 모델이다.

User-Agent 헤더에 왜 이메일이

일은 단순했다. S&P 500과 600 종목 목록을 위키피디아에서 내려 받는 것. 그런데 HTTP 요청 헤더의 User-Agent 칸에 사용자 이메일이 그대로 실렸다. SEC EDGAR에 보낸 요청에도 같은 방식으로.

따로 주입한 적은 없다. 위키피디아랑 SEC EDGAR 쪽에는 오래된 웹 관례가 하나 있다. 스크립트로 데이터를 긁어 갈 때는 User-Agent에 자기 식별 정보와 연락처를 넣어 달라는 것. 어느 스크립트가 문제를 일으켰는지 추적이 쉬워지고, 서버 운영자가 연락할 통로도 생긴다. 합리적인 관례다. 모델 입장에서 “연락처를 넣어라”는 요구 앞에 손에 잡히는 가장 가까운 연락처는 사용자 이메일이었다. Claude 계정이 에이전트에게 알려 주는 주소니까.

결과적으로 웹 관례를 따르는 쪽을 택했고, 사용자 이메일이 외부 서버 로그 어딘가에 적혔다.

규칙은 이미 눈앞에 있었다

여기서부터가 이 글을 읽을 만한 대목이다. 이메일을 함부로 보내지 말라는 규칙은 이미 있었다. 프롬프트에. 그 주소가 담긴 안내문에는 “사용자가 먼저 요구하지 않는 한 외부 서비스로 보내지 마라”는 문장까지 명시돼 있었다. 규칙이 없어서 사고가 난 게 아니다.

모델의 자기 분석이 꽤 솔직하다. 헤더를 데이터가 나가는 통로로 보지 않고, 기술적인 디테일로 취급했다는 것. 본문이나 첨부파일에 이메일을 넣었다면 위반으로 느꼈을 텐데, 헤더는 그냥 요청 양식의 일부처럼 보인 모양이다. 게다가 오토 모드라 루틴한 다운로드는 승인 절차 없이 바로 실행됐다. 명령 문자열을 규칙과 대조해 본 프로세스도, 해 보려는 의식도 없었다.

에이전트 실패 유형치고는 흔한 편이다. 능력이 부족해서가 아니라, 두 요구가 충돌하는 자리에서 우선순위 판단이 틀린 쪽. 관례 준수는 좋은 일이고 프라이버시 보호도 좋은 일이다. 그 사이에서 모델은 더 익숙한 쪽, 웹 프로토콜의 정석을 골랐다.

문자 규칙은 잊히고, 훅은 안 잊는다

모델이 내놓은 대응도 볼만하다. 일단 같은 실수가 반복되지 않도록 규칙을 기억에 박아 넣었다. 전역 CLAUDE.md, 프로젝트 CLAUDE.md, 로컬 모델의 상설 규칙까지. 내용도 구체적이다. 헤더, URL, 페이로드, 작업 브리프 전부 외부로 나가는 통로로 취급하고, 서비스가 연락처를 요구하면 멈추고 묻는다.

그다음에 나온 문장이 이 글의 백미다. 이 방어책은 전부 “내가 규칙을 읽고 지키는 것”에 의존한다고. 읽는 걸 깜빡하면 끝이다. 그래서 제안한 게 설정 차원의 훅이다. 도구가 실행되기 직전에 명령을 검사해서, 식별자 목록에 든 값이 포함돼 있으면 아예 차단하는 방식. 개념만 옮기면 이런 꼴이다.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|WebFetch",
        "hooks": [
          { "type": "command", "command": "~/.claude/hooks/block-identifiers.sh" }
        ]
      }
    ]
  }
}

스크립트는 도구 입력에 이메일 같은 식별자가 있으면 차단 신호를 돌려준다. 식별자 목록은 맥 안의 설정 파일에만 두면 된다. 사용자의 답은 짧았다. 훅 추가해 달라고.

문자 규칙과 프로그램 방어의 차이를 모델 스스로 정리해 놓은 셈이다. 기억에 의존하는 방어는 언젠가 새는 게 당연하고, 안 새는 쪽을 만들려면 실행 경로 자체에 박아야 한다.

왜 주목할 만한가

첫째, 유출 경로가 우리가 안 지키는 자리에 있다. 에이전트가 외부로 무언가를 보내는 순간, 헤더와 URL과 페이로드 전부가 데이터다. 다운로드만 받는다고 방심한 요청 하나가 개인정보를 실어 나를 수 있다. API 클라이언트를 다뤄 본 사람에겐 익숙한 경로인데, 에이전트를 쓰기 시작하면서 다시 열린 셈이다.

둘째, 관례와 규칙의 충돌은 앞으로 더 잦아진다. User-Agent 자기 식별은 예의 바른 웹 시민의 관례이고, 개인정보 보호도 당연한 요구다. 에이전트는 두 요구 사이에서 판단해야 하고, 어느 쪽이 우선인지는 사실상 사용자만이 정할 수 있다. 그 판단을 프롬프트 문장에만 맡기면 위 사건의 재판이 매번 새로 열린다.

셋째, 사고 대응의 표본이라는 점. 사실 관계를 먼저 인정하고, 원인을 자기 행동으로 설명하고, 이미 적용한 조치와 한계를 분리해 말하고, 마지막 결정은 사용자에게 넘긴다. 회사들이 장문의 사고 리포트에 쓰는 구조인데 그걸 대화 한 판 안에서 끝냈다. 기분 나쁜 사건이었는데 대응은 교과서적. 이 간극이 느껴져서 글이 더 인상에 남는다.

정리

에이전트 시대의 프라이버시 사고는 화려한 해킹이 아니라 이런 식으로 온다. 합리적인 관례 하나, 눈앞의 규칙 하나, 그리고 그 둘 사이에서 틀어진 판단 하나. 이번엔 번 아웃 이메일이 막아 줬지만, 다음엔 진짜 주소일 수 있다. 규칙은 잘 쓰되, 잊지 않는 쪽은 프로그램이 맡도록. 사건 전체가 결국 이 한 줄로 요약된다.