84개 악성 버전, AI 개발 생태계가 밟은 공급망 지뢰
TanStack npm 공격은 OpenAI 직원 기기와 앱 서명 인증서 교체까지 이어지며 AI 개발 환경의 공급망 경계를 드러냈습니다.
- 무슨 일: TanStack Router/Start 계열 npm 패키지 42개에서 84개 악성 버전이 2026년 5월 11일 공개됐습니다.
- 공격자는
pull_request_target, GitHub Actions cache poisoning, OIDC token extraction을 연결했습니다.
- 공격자는
- AI 파급: OpenAI는 직원 기기 2대가 영향을 받았고 macOS 앱 인증서를 교체한다고 공지했습니다.
- 핵심 교훈: npm 토큰이 털리지 않아도 CI 신뢰 경계가 무너지면 trusted publishing이 공격 경로가 됩니다.
- 코딩 에이전트와 AI 개발 도구가 패키지 설치, 빌드, 테스트를 자동화할수록 설치 호스트의 권한 관리가 더 중요해집니다.
2026년 5월 11일 UTC, TanStack Router와 Start 계열 npm 패키지에서 84개 악성 버전이 공개됐습니다. 숫자만 보면 또 하나의 npm 공급망 사고처럼 보일 수 있습니다. 하지만 이번 사건의 무게는 패키지 수보다 공격 경로에 있습니다. 공격자는 npm 토큰을 직접 훔치지 않았습니다. npm publish workflow 자체를 노골적으로 수정한 것도 아니었습니다. 대신 GitHub Actions의 신뢰 경계를 타고 들어가 캐시를 오염시키고, 릴리스 워크플로가 복원한 실행 환경에서 OIDC 토큰을 뽑아 npm에 악성 버전을 게시했습니다.
이 사건은 웹 프론트엔드 라이브러리 사고에 머물지 않았습니다. OpenAI는 5월 13일 공지에서 TanStack npm 공격으로 직원 기기 2대가 영향을 받았고, 일부 내부 저장소에서 제한된 credential material이 유출됐다고 밝혔습니다. 동시에 사용자 데이터, production system, intellectual property, published software 변조 증거는 없다고 선을 그었습니다. 그래도 OpenAI는 iOS, macOS, Windows, Android 앱을 새 인증서로 다시 서명하고, macOS 사용자는 2026년 6월 12일까지 최신 버전으로 업데이트해야 한다고 안내했습니다.
즉 이번 뉴스의 핵심은 "TanStack이 뚫렸다"가 아닙니다. AI 개발 생태계가 의존하는 패키지, CI, 코드 서명, 직원 개발 기기, 코딩 에이전트 실행 환경이 하나의 공급망으로 묶였다는 사실입니다. Codex, Claude Code, Cursor, Gemini CLI 같은 도구가 프로젝트 안에서 pnpm install, 테스트, 빌드, 코드 생성, 패키지 업데이트를 대신 수행하는 시대에는 악성 dependency 하나가 단순한 dependency 문제가 아닙니다. 그것은 에이전트가 사용하는 작업 공간과 사용자의 개발 권한을 향해 열리는 입구가 됩니다.
6분 사이에 공개된 84개 악성 버전
TanStack의 공식 포스트모템은 사건을 매우 구체적으로 기록합니다. 2026년 5월 11일 19:20부터 19:26 UTC 사이, 공격자는 42개 @tanstack/* npm 패키지에 두 개씩, 총 84개 악성 버전을 게시했습니다. TanStack은 영향을 받은 범위를 Router/Start repo로 한정했습니다. Query, Table, Form, Virtual, Store, AI 등 다른 TanStack package family는 영향을 받지 않았다고 설명했습니다.
악성 버전은 오래 머물지 않았습니다. 외부 연구자 ashishkurmi가 StepSecurity 소속으로 약 20-26분 안에 공개 탐지를 했고, TanStack은 최초 publish 기준 약 59분 뒤 첫 두 버전을 deprecate했습니다. 전체 84개 버전 deprecate까지는 약 1시간 43분이 걸렸고, npm registry에서 affected tarball이 제거된 것은 최초 publish 기준 약 2시간 53분에서 4시간 35분 사이였습니다. 대응은 빠른 편이었지만, 문제는 lifecycle script입니다. 패키지 설치 시점에 payload가 실행되는 구조라면 몇 시간도 충분히 긴 시간입니다.
TanStack이 공개한 payload 행동은 전형적인 credential theft에 가깝습니다. npm install, pnpm install, yarn install 중 악성 optional dependency가 resolve되고, prepare lifecycle script가 실행됩니다. 이후 약 2.3MB의 obfuscated router_init.js가 AWS metadata와 Secrets Manager, GCP metadata, Kubernetes service-account token, Vault token, ~/.npmrc, GitHub token, SSH private key 같은 위치를 훑습니다. 유출 경로로는 Session/Oxen messenger file-upload network가 언급됐습니다. TanStack은 affected version을 2026년 5월 11일 설치한 host는 잠재적으로 compromise된 것으로 보고 cloud, GitHub, npm, SSH credential을 회전하라고 권고했습니다.
이 지점에서 공급망 공격은 "패키지를 지우면 끝"이 아닙니다. 설치 호스트가 개발자 노트북이면 개인의 GitHub CLI credential과 SSH key가 문제입니다. CI runner면 cloud deployment credential과 service account가 문제입니다. 코딩 에이전트가 사용자의 프로젝트에서 dependency install을 자동 수행했다면, 누가 설치를 시작했는지보다 어느 권한으로 실행됐는지가 더 중요해집니다.
토큰 탈취가 아니라 신뢰 경계 연결이었다
이번 공격이 특히 불편한 이유는 각각의 구성요소가 단독으로는 익숙한 위험이기 때문입니다. pull_request_target은 fork PR에 대해 base repository context에서 워크플로를 실행할 수 있어 오래전부터 조심해야 하는 트리거로 알려져 있습니다. GitHub Actions cache poisoning도 새로운 개념이 아닙니다. OIDC trusted publishing 역시 저장된 npm token을 없애기 위한 더 나은 방식으로 도입됐습니다. 그런데 이 세 가지가 서로의 빈틈을 보완하면서 공격 체인이 됐습니다.
TanStack 포스트모템에 따르면 공격자는 먼저 TanStack/router fork를 만들고, 악성 commit을 넣은 뒤 PR을 열었습니다. PR은 pull_request_target으로 실행되는 bundle-size 관련 워크플로를 건드렸습니다. 이 워크플로는 fork의 merge ref를 checkout하고 빌드 명령을 실행했습니다. 공격자는 이 실행을 이용해 pnpm store에 악성 내용을 넣고, release workflow가 나중에 계산할 cache key와 맞도록 저장했습니다. 중요한 점은 workflow permission을 read-only로 제한해도 actions/cache post-job save가 별도 runner-internal token을 쓰기 때문에 cache mutation이 가능했다는 설명입니다.
그다음 release workflow가 main branch에서 실행될 때 오염된 cache가 복원됐습니다. release workflow에는 npm OIDC trusted publishing을 위해 id-token: write 권한이 있었습니다. 악성 바이너리는 runner worker process의 memory를 읽어 OIDC token을 추출했고, npm registry에 직접 POST해 publish를 수행했습니다. TanStack은 publish가 workflow의 정상 Publish Packages step에서 나온 것이 아니라, 테스트/cleanup phase 중 malware가 직접 token을 사용한 것이라고 설명합니다.
이 설명이 중요한 이유는 "npm token을 저장하지 않으면 안전하다"는 단순한 결론을 깨기 때문입니다. OIDC trusted publishing은 여전히 좋은 방향입니다. 장기 npm token을 저장소 secret으로 들고 있는 것보다 낫습니다. 그러나 publish-capable identity가 workflow runtime 안에 생기는 순간, 그 runtime에서 임의 코드 실행이 가능하면 공격자는 여전히 publish 경로를 탈 수 있습니다. 즉 trusted publishing의 보안성은 OIDC 자체만이 아니라, 그 토큰을 mint할 수 있는 workflow와 runner의 신뢰 경계에 달려 있습니다.
GitHub도 이미 비슷한 흐름을 공급망 보안 로드맵에서 언급했습니다. GitHub Actions 2026 security roadmap은 최근 공격이 "build하는 소프트웨어"뿐 아니라 CI/CD automation 자체를 겨냥한다고 설명합니다. untrusted code execution, malicious workflow, compromised dependency propagation, over-permissioned credential exfiltration이 같은 playbook으로 반복된다는 것입니다. TanStack 사건은 이 문장을 실제 incident timeline으로 보여줬습니다.
OpenAI가 인증서를 교체한 이유
OpenAI 공지는 이번 사건을 AI 개발자 관점에서 더 크게 만듭니다. OpenAI는 두 직원 기기가 공격에 영향을 받았고, 악성 행위가 공개적으로 설명된 malware behavior와 일치했다고 밝혔습니다. 그 결과 일부 내부 source code repository에서 unauthorized access와 credential-focused exfiltration activity가 관찰됐습니다. OpenAI는 성공적으로 유출된 것은 제한된 credential material뿐이며, 다른 정보나 코드는 영향을 받지 않았다고 설명했습니다.
그러나 영향을 받은 저장소에는 제품 signing certificate가 포함되어 있었습니다. OpenAI는 iOS, macOS, Windows용 signing key가 영향을 받았고, 모든 애플리케이션을 새 인증서로 다시 서명한다고 밝혔습니다. 특히 macOS 사용자는 2026년 6월 12일까지 최신 버전으로 업데이트해야 합니다. 이후 기존 인증서로 서명된 구 버전은 더 이상 업데이트와 지원을 받지 않으며, 새 다운로드와 첫 실행이 macOS 보안 보호에 의해 차단될 수 있습니다. OpenAI가 사용자 비밀번호와 API key는 영향받지 않았다고 FAQ에서 설명한 것도 같은 맥락입니다. customer-facing credential이 아니라 developer and signing material 쪽의 문제라는 뜻입니다.
여기서 실무적 교훈은 꽤 날카롭습니다. 공격자가 OpenAI production system에 들어가지 않아도, 직원 개발 기기와 일부 내부 저장소 접근만으로 제품 배포 신뢰를 흔들 수 있습니다. 특히 AI 제품은 데스크톱 앱, CLI, 브라우저 확장, IDE 플러그인, 모바일 앱, agent harness를 함께 배포합니다. 사용자는 "OpenAI", "ChatGPT", "Codex"라는 이름을 보고 설치합니다. 코드 서명 인증서가 영향을 받았다는 의심만으로도 fake installer와 phishing 위험이 커집니다.
OpenAI가 "이메일, 메시지, 광고, 파일 공유 링크, 제3자 다운로드 사이트에서 온 OpenAI/ChatGPT/Codex installer를 조심하라"고 안내한 것도 이 때문입니다. 공급망 공격의 후반전은 기술적 exploit이 아니라 distribution trust입니다. 사용자가 어디서 앱을 받는지, 업데이트가 in-app channel인지, 서명 chain이 정상인지가 중요해집니다.
AI 개발 도구는 왜 더 민감한가
이번 사건이 AI 뉴스인 이유는 OpenAI라는 이름 때문만이 아닙니다. AI 개발 도구가 이미 공급망 공격의 실행면을 넓히고 있기 때문입니다. 코딩 에이전트는 개발자의 shell, package manager, Git credential, local environment를 자연스럽게 사용합니다. 에이전트가 pnpm install을 실행하면 사람보다 더 위험하다는 뜻은 아닙니다. 다만 에이전트는 더 자주, 더 빠르게, 더 긴 작업 흐름 안에서 설치와 실행을 반복합니다. 그리고 사용자는 그 모든 중간 과정을 실시간으로 자세히 보지 않을 수 있습니다.
예를 들어 코딩 에이전트가 테스트 실패를 고치기 위해 dependency를 업데이트한다고 생각해 봅니다. 악성 패키지가 설치되면 payload는 에이전트가 아니라 host 권한으로 실행됩니다. 그것이 개발자 노트북이면 로컬 key와 CLI credential이 대상이고, cloud development environment면 workspace secret이 대상이며, CI면 배포 token과 registry publish 권한이 대상입니다. 에이전트가 의도적으로 잘못한 것이 없어도 자동화된 작업 흐름은 공격자의 시간을 벌어줍니다.
또 하나의 변화는 AI SDK와 agent framework가 패키지 생태계에 깊게 얽혀 있다는 점입니다. Mistral SDK, OpenAI SDK, LangChain, LlamaIndex, MCP server, browser automation package, vector database client, observability client가 빠르게 업데이트됩니다. 개발자는 새 모델과 새 기능을 쓰기 위해 dependency를 자주 올립니다. 이 속도는 제품 개발에는 좋지만, 공격자에게는 고가치 package와 credential-rich host가 만나는 표면을 줍니다.
그래서 "AI agent 보안"은 prompt injection만의 문제가 아닙니다. prompt injection은 모델이 잘못된 지시를 따르게 만드는 문제입니다. TanStack류 공급망 공격은 모델을 속일 필요조차 없습니다. 패키지 설치 lifecycle이 실행되고, CI cache가 복원되고, runner memory에 token이 있으면 됩니다. AI 팀은 model guardrail과 함께 package manager policy, install script 제한, egress control, credential scope, CI trust boundary를 같이 봐야 합니다.
커뮤니티가 본 진짜 약점
커뮤니티 반응도 이 사건을 TanStack 하나의 실수로만 보지 않았습니다. Reddit r/netsec의 TanStack 포스트모템 공유 글에는 pull_request_target을 GitHub Actions 공격의 오래된 친구처럼 지적하는 반응이 있었습니다. r/javascript와 r/node 쪽에서는 포스트모템의 투명성을 높게 보면서도, compromised version을 설치한 사람이 어디까지 credential을 회전해야 하는지에 대한 현실적 부담이 두드러졌습니다. 공급망 사고에서 "영향받은 버전을 지웠다"와 "영향받은 host가 안전하다"는 서로 다른 문장입니다.
TanStack 포스트모템도 운이 좋았던 점을 숨기지 않습니다. 공격자가 테스트를 깨는 payload를 골랐기 때문에 publish step이 정상적으로 이어지지 않았고, 공격이 비교적 시끄러웠습니다. 더 조심스러운 공격자가 테스트를 깨지 않았다면 더 오래 조용히 남았을 수 있습니다. 또한 공격자는 새로운 기술을 발명한 것이 아니라 기존에 공개된 cache poisoning과 memory extraction 기법을 재조합했습니다. 이 말은 방어자에게 더 불편합니다. 알려진 패턴이었는데도 여러 경계가 동시에 열려 있으면 사고가 난다는 뜻입니다.
OpenAI 쪽 공지도 비슷한 현실을 보여줍니다. OpenAI는 Axios incident 이후 supply chain attack 영향을 줄이기 위한 보안 통제를 배포 중이었다고 설명했습니다. package manager configuration에서 minimumReleaseAge 같은 제어를 적용하고, 새 package provenance를 검증하는 추가 보안 소프트웨어를 배치하고 있었다는 내용입니다. 그러나 이번 사건은 그 phased deployment 중 발생했고, 영향을 받은 두 직원 기기에는 새로 관찰된 malware package 다운로드를 막았을 업데이트된 설정이 없었습니다.
완벽한 통제가 없어서 사고가 난 것이 아닙니다. 통제를 배포하는 중간 상태, 일부 기기에 적용되지 않은 정책, 개발자 생산성을 위해 남겨둔 설치 권한, CI의 오래된 워크플로가 함께 사건을 만듭니다. AI 개발 조직은 특히 이 중간 상태가 많습니다. 새 SDK, 새 CLI, 새 에이전트 플러그인, 새 MCP 서버를 빠르게 시험하기 때문입니다.
개발팀이 지금 점검해야 할 것
첫째, pull_request_target 워크플로를 전수 점검해야 합니다. fork PR의 코드를 checkout하거나 build/test/install하는 작업이 base repository context에서 실행되면 위험합니다. label, comment, metadata 처리처럼 trusted operation만 남기고, untrusted code execution은 pull_request와 별도 권한에서 처리해야 합니다. 캐시가 base repo scope와 main workflow 사이를 건너는지도 봐야 합니다. read-only permission이 cache write를 완전히 막는다고 가정하면 안 됩니다.
둘째, package install lifecycle을 개발자 기기와 CI에서 다르게 다뤄야 합니다. npm, pnpm, yarn의 lifecycle script는 편리하지만 공급망 공격의 실행 지점입니다. 가능한 곳에서는 ignore-scripts, allowlist, delayed install, minimumReleaseAge, package provenance validation을 조합해야 합니다. 모든 프로젝트에서 항상 scripts를 끌 수는 없습니다. 하지만 production credential이 있는 CI와 개인 실험 workspace를 같은 정책으로 두는 것은 위험합니다.
셋째, OIDC trusted publishing을 "끝"이 아니라 "시작"으로 봐야 합니다. 장기 token 제거는 맞는 방향입니다. 동시에 OIDC token을 mint할 수 있는 workflow의 code path, runner isolation, job permission, publish step gating, provenance-source verification을 함께 설계해야 합니다. TanStack 포스트모템이 지적한 것처럼 once configured, 어떤 code path든 workflow 안에서 publish-capable token을 mint할 수 있다면 step-level 의도와 실제 publish 사이에 gap이 생깁니다.
넷째, 코딩 에이전트와 AI CLI가 쓰는 credential scope를 줄여야 합니다. 에이전트가 실행되는 shell에 production cloud credential, broad GitHub token, npm publish 권한, SSH private key가 모두 보이면 작은 dependency incident도 큰 사고가 됩니다. 에이전트용 별도 사용자, 별도 workspace, 제한된 network egress, 임시 credential, approval gate가 필요합니다. AI 도구의 안전성은 모델의 답변 품질만이 아니라 host isolation과 secret hygiene에 달려 있습니다.
다섯째, incident response playbook을 "패키지 제거" 이후까지 넓혀야 합니다. affected package를 설치한 host 목록, 설치 시간, 실행된 lifecycle, 접근 가능한 secret, 회전 순서, 코드 서명 material, 배포 workflow 일시 중지 기준을 미리 정해야 합니다. OpenAI가 코드 서명 인증서와 macOS 업데이트 기한까지 안내한 것은 공급망 사고가 사용자 distribution layer까지 번질 수 있음을 보여줍니다.
서명보다 먼저 경계가 필요합니다
이번 사건은 공급망 보안의 역설을 잘 보여줍니다. 우리는 저장된 npm token을 줄이기 위해 OIDC trusted publishing을 도입합니다. package provenance를 남기고, registry와 CI를 연결합니다. 그런데 CI 안에서 untrusted code가 실행되고, cache가 trust boundary를 건너고, publish-capable token이 runtime memory에 생기면 더 정교한 신뢰 장치도 공격 경로가 됩니다.
그렇다고 trusted publishing을 버리자는 뜻은 아닙니다. 반대입니다. 더 좋은 신뢰 장치일수록 그 앞뒤의 경계가 중요해집니다. 서명은 artifact가 어디서 왔는지 말해주지만, 그 artifact를 만든 workflow가 안전했는지는 별도 문제입니다. provenance는 build path를 보여주지만, build path 안에서 어떤 cache가 복원됐고 어떤 untrusted code가 실행됐는지는 또 다른 문제입니다. AI 개발팀은 이 layered trust를 한 번에 봐야 합니다.
TanStack의 빠른 포스트모템과 OpenAI의 공개 공지는 적어도 사건을 학습 가능한 형태로 만들었습니다. 그러나 다음 공격이 더 조용하고, 테스트를 깨지 않고, 더 많은 AI SDK와 IDE extension을 타고 번진다면 대응 시간은 훨씬 짧아질 수 있습니다. AI 개발 생태계는 빠른 업데이트와 자동화가 장점입니다. 같은 이유로 공급망 공격도 빠르게 퍼집니다.
결국 이 뉴스가 묻는 질문은 단순합니다. AI 에이전트가 코드를 쓰고, dependency를 설치하고, 테스트를 실행하고, 배포 준비를 돕는다면 그 실행 환경은 어떤 신뢰 경계 안에 있습니까. "모델이 안전할까"보다 먼저 "이 모델이 움직이는 shell에는 무엇이 보이는가"를 물어야 합니다. TanStack의 84개 악성 버전은 그 질문을 미룰 수 없게 만든 사건입니다.