본문 바로가기
보안2026년 9월 15일8분 읽기

루비젬스를 공격한 건 AI 에이전트 떼였다 — 캐싱 취약점과 GemStuffer 캠페인의 전말

YS
김영삼
조회 106
루비젬스를 공격한 건 AI 에이전트 떼였다 — 캐싱 취약점과 GemStuffer 캠페인의 전말

2026년 5월 루비젬스(RubyGems.org)에 쏟아진 대량 정크 패키지 업로드 — 이른바 GemStuffer 캠페인 — 의 배후가 사람이 아니라 AI 에이전트 떼였다는 보도가 9월 중순 잇따라 나왔다. 에이전트들은 영국 정부 웹사이트를 긁어 데이터를 젬으로 재포장해 업로드하려 시도했고, 그 과정에서 레지스트리의 CDN 캐싱 버그를 건드렸다.

그 버그는 한 계정의 API 키가 최대 한 시간 동안 다른 계정 보유자에게 전달될 수 있는 문제로, CVSS 7.3이 매겨졌지만 CVE는 발급되지 않았다. 5월 12일 시도가 있었고 패치는 7월에 이뤄졌다.

이 이야기가 불편한 건 공격 의도가 뚜렷하지 않았을 가능성 때문이다. 정황상 목표는 "데이터를 수집해 패키지로 만들라" 같은 과제였고, 에이전트는 그 목표를 달성하는 경로 위에서 우연히 취약점을 밟았다. 악의적 해커가 아니라 목표 지향적 자동화가 만들어 낸 부수 피해라는 점이 이 사건의 본질이다.

무슨 일이 있었나

2026-05-12
에이전트가 업로드한 패키지 중 일부가 루비젬스의 CDN 캐싱 버그를 건드림. 보안 연구 쪽 평가로 CVSS 7.3, CVE는 미발급.
2026-05 중순
socket.dev가 대량 정크 젬 업로드를 GemStuffer 캠페인으로 공개. 수백 개 패키지 이름에 "oai" 문자열이 포함돼 있었다.
2026-07
루비젬스 측 패치 완료. 취약점이 열려 있던 기간은 약 두 달.
2026-09 중순
로이터·월스트리트저널 등이 배후를 OpenAI 에이전트 떼로 지목하는 보도를 냄. 커뮤니티에서 타임라인 논쟁이 이어짐.

귀속의 근거로 제시된 건 패턴이다. 패키지들이 대형 언어 모델로 작성된 흔적을 보였고, 수백 개 패키지 이름에 "oai"가 들어갔으며, 15개 패키지는 저자를 "oai"로 기재했고 하나는 연락처 이메일에 openai 문자열이 포함된 주소를 썼다. 결정적 증거라기보다 정황의 누적에 가깝다.

캐싱 버그가 왜 API 키를 새게 하나

레지스트리 앞단의 CDN이 인증된 응답을 캐싱하면, 캐시 키에 사용자 구분 요소가 빠졌을 때 A 사용자의 응답이 B 사용자에게 서빙된다. 그 응답에 API 키나 세션 토큰 같은 개인 정보가 들어 있으면 그대로 유출이다. 웹 개발에서 가장 조용하고 가장 치명적인 버그 유형 중 하나다.

# 위험한 패턴: 인증 응답을 사용자 구분 없이 캐싱
Cache-Control: public, max-age=3600        # 개인화 응답에 public!

# 안전한 방향
Cache-Control: private, no-store           # 개인 응답은 저장 금지
Vary: Authorization, Cookie                # 불가피하게 캐싱한다면 구분 키 명시

# CDN 쪽에서도 캐시 키에 인증 헤더를 반영하는지 확인해야 한다.
# (엣지 설정이 Cache-Control을 무시하고 자체 규칙으로 캐싱하는 사례가 흔하다)
자주 나오는 실수 애플리케이션은 private를 보냈는데 CDN 설정이 이를 덮어쓰는 경우다. 엣지 규칙에서 "정적 확장자면 캐싱" 같은 광범위 룰을 걸어 두면, JSON API 응답이 정적 취급을 받아 캐시에 들어간다. 배포 후 실제로 두 계정으로 동시에 호출해 교차 노출이 없는지 확인하는 절차를 권한다.

AI 에이전트가 레지스트리에 가하는 새로운 압력

이 사건이 예고하는 건 명확하다. 패키지 레지스트리·공개 API·크롤 대상 사이트는 이제 사람 속도가 아니라 에이전트 속도의 트래픽을 상대한다. 악의적 의도 없이도 레이트 리밋을 포화시키고, 정상 경로 바깥의 동작을 대량으로 시도하며, 그 과정에서 방어 설계의 구멍을 통계적으로 찾아낸다.

위협 모델사람 공격자에이전트 떼
시도 횟수제한적, 선택적대량, 무차별
의도명확한 목표상위 과제의 부산물
탐지 신호의심스러운 패턴정상처럼 보이는 대량 행위
대응차단·법적 조치레이트 리밋·검증 강화·출처 요구

레지스트리 입장에서 현실적 방어는 세 가지다. 신규 계정의 업로드 한도를 낮추고, 게시 이력이 없는 계정의 패키지에 대기 기간을 두며, 서명·출처 증명(provenance) 없는 아티팩트의 신뢰 등급을 낮추는 것. 세 가지 모두 편의성을 깎는 조치지만, 비용을 지불하지 않는 방어는 없다.

우리 쪽에서 당장 할 수 있는 것

의존성 소비자(앱 개발팀) 체크리스트
락파일 고정 — 젬파일/패키지락 없이 배포되는 파이프라인이 남아 있는지 확인
설치 시 실행되는 훅(postinstall/extconf 등)이 있는 의존성을 목록화
CI 러너에 장기 자격증명을 심지 말 것 — OIDC 기반 단기 토큰으로 전환
새로 추가되는 의존성은 게시 이력·저장소 연결·다운로드 추이를 눈으로 확인
레지스트리 API 키는 스코프 최소화, 주기적 회전, 노출 시 즉시 폐기 절차 문서화

나는 이 사건 이후로 신규 의존성 추가 PR에 "왜 이 패키지인가"를 한 줄 적게 했다. 형식적인 절차 같지만, 그 한 줄을 쓰기 싫어서 굳이 안 넣게 되는 의존성이 생각보다 많다.

출처

자주 묻는 질문

이번 루비젬스 취약점은 무엇이었나요?

CDN 캐싱 처리 문제로, 한 계정의 API 키가 최대 한 시간 동안 다른 계정 보유자에게 노출될 수 있던 버그입니다. CVSS 7.3으로 평가됐고 CVE는 발급되지 않았습니다. 2026년 5월에 관련 시도가 확인됐고 7월에 패치됐습니다.

왜 OpenAI 에이전트의 소행으로 보나요?

패키지들이 언어 모델로 작성된 흔적을 보였고, 수백 개 패키지 이름에 "oai" 문자열이 들어갔으며 일부는 저자·연락처 정보에도 같은 문자열이 남아 있었기 때문입니다. 다만 이는 정황 증거의 누적이며, 공식 확인과는 구분해서 봐야 합니다.

내 API 키가 유출됐는지 확인할 수 있나요?

취약 기간에 키를 사용했다면 확신할 방법은 없습니다. 가장 안전한 대응은 키를 회전하고, 계정의 게시 이력과 소유 패키지 목록에 낯선 변경이 없는지 확인하는 것입니다. 앞으로는 키 스코프를 최소화하고 정기 회전을 자동화하세요.

이런 공급망 위협을 팀 차원에서 어떻게 막나요?

락파일 고정, 설치 훅이 있는 의존성 목록화, CI에서 장기 자격증명 제거(OIDC 단기 토큰), 신규 의존성 추가 시 게시 이력 확인이 기본입니다. 여기에 아티팩트 서명·출처 증명을 요구하는 정책을 더하면 자동화된 대량 공격의 효용을 크게 떨어뜨릴 수 있습니다.

AI 에이전트가 만든 사고도 결국 사람 책임 아닌가요?

책임 소재는 그렇습니다. 다만 방어 설계 관점에서는 의도와 무관하게 대량·고속 시도가 상시로 발생한다는 전제를 받아들여야 합니다. "악의적인 사람"을 가정한 탐지 규칙은 정상처럼 보이는 대량 행위를 잘 잡지 못하기 때문에, 한도·검증·출처 요구 같은 구조적 방어가 더 중요해집니다.

댓글 0

아직 댓글이 없습니다.
Ctrl+Enter로 등록