2026-10-13 수정 미니 샤이-훌루드의 첫 확인 시점(2026년 4월 29일 SAP 패키지)과 공격 방식(가짜 패키지가 아니라 정상 패키지의 배포 경로 탈취), 실행 지점(postinstall이 아니라 preinstall·prepare), TanStack 악성 버전 84개가 생긴 경위, 소스 공개 시점(2026년 5월 12일 무렵 TeamPCP가 직접 공개), 미아즈마의 출처와 날짜(Wiz, 2026년 6월 1일), 옥스시큐리티가 보고한 패키지 4종의 성격(한 공격자가 올렸고 공개 코드 복제본은 chalk-tempalte 하나)을 바로잡았다. 과장된 표현('설치 1초'·'토큰 하나로 클라우드 전체가 열리는 구조'·'SBOM으로 즉시 추적'·'수 시간 내에 만료')과 근거 없는 '대부분의 개발팀' 일반화를 고치거나 지우고, npm audit 설명과 점검 명령, 'pip.lock' 표기를 고쳤다. npm 12부터 의존성 설치 스크립트가 기본으로 차단된다는 점, 같은 계열이었던 Bitwarden 건 설명, 토큰 교체 전에 지속 장치(gh-token-monitor)부터 지워야 한다는 안전 경고, 실제 제공 범위에 맞춘 문의 안내와 참고 자료를 반영했다.

미니 샤이-훌루드(Mini Shai-Hulud)는 정상 패키지의 배포 경로(유지관리자 계정, CI/CD, OIDC 토큰)를 탈취해 npm·PyPI에 악성 버전을 올리고, 설치한 개발자·CI의 자격증명으로 다른 패키지까지 감염시키는 자가 전파 웜이다. 패키지 설치와 동시에 숨겨진 스크립트가 실행되어 GitHub 토큰·AWS 키·npm 배포 토큰을 탈취하고, 이를 발판 삼아 CI/CD 파이프라인과 클라우드 계정까지 침투한다.

'미니 샤이-훌루드'라는 이름의 작전은 2026년 4월 29일 SAP 관련 npm 패키지에서 처음 확인됐고 (출처: Wiz, 2026-04-29), 5월 11~12일(UTC) TanStack을 시작으로 Mistral AI·UiPath·OpenSearch·Guardrails AI 등 개발자들이 많이 쓰는 패키지로 번졌다 (출처: 데일리시큐, 2026-05-14). 6월 1일에는 공개된 코드를 바탕으로 한 것으로 보이는 '미아즈마(Miasma)' 변종이 레드햇의 @redhat-cloud-services npm 패키지까지 오염시켰다 (출처: Wiz, 2026-06-01). TanStack 악성 버전에는 GitHub 보안 권고 GHSA-g7cv-rxg3-hmpx(CVE-2026-45321)가 나왔고, 다른 패키지의 공식 권고도 GitHub Security Advisories(GHSA)에서 패키지명으로 조회할 수 있다.

SENTRIX는 보안 실무 10년 레드팀이 운영하는 보안 전문 기업이다. 이 글은 공격 체인의 전 과정을 분해하고, 개발팀과 DevOps가 지금 바로 적용할 수 있는 방어 체계를 정리한다.


한눈에 보는 미니 샤이-훌루드

Q. 미니 샤이-훌루드가 정확히 무엇인가요?
A. 공격 그룹 TeamPCP가 npm·PyPI 공급망 공격에 쓴 자가 전파형 악성코드입니다. 가짜 패키지를 만드는 대신 진짜 패키지의 배포 경로(유지관리자 계정, CI/CD, OIDC 토큰)를 탈취해 악성 버전을 올렸기 때문에 패키지 이름이 정상이고, 일부는 정상 출처 증명(SLSA)까지 붙어 있었습니다. TeamPCP가 2026년 5월 12일 무렵 소스 코드를 GitHub에 직접 공개한 뒤에는 모방 공격자가 철자가 비슷한 가짜 패키지(타이포스쿼팅)에 이 코드를 넣어 퍼뜨린 사례도 나왔습니다 (출처: 옥스시큐리티, 2026-05-12, 데일리시큐, 2026-05-19).
Q. 어떻게 전파되나요?
A. 개발자나 CI가 악성 버전을 설치하는 순간 설치 단계 스크립트가 자동 실행됩니다. 추가 파일 실행 없이 설치만으로 탈취가 시작됩니다. SAP·UiPath 공격은 preinstall 스크립트(node setup.mjs가 Bun 런타임을 내려받아 악성코드 실행)를, TanStack 공격은 optionalDependencies가 가리키는 GitHub 커밋의 prepare 스크립트를 썼습니다. TanStack·레드햇 건은 GitHub Actions의 OIDC 토큰으로 게시했고, 웜은 훔친 npm 토큰이나 OIDC 토큰으로 피해자가 관리하는 다른 패키지를 다시 게시했습니다. TanStack 건에서 공격자는 pull_request_target 워크플로로 Actions 캐시를 오염시킨 뒤, 릴리스 워크플로가 실행될 때 러너 프로세스 메모리에서 OIDC 토큰을 빼내 정상 '신뢰된 게시자' 자격으로 게시했습니다. npm 토큰은 탈취되지 않았습니다.
Q. 어떤 데이터를 빼가나요?
A. GitHub 토큰, npm 배포 토큰, AWS/Azure/GCP 자격증명, Kubernetes 서비스 계정 토큰, HashiCorp Vault 인증정보, SSH 키가 주요 탈취 대상입니다. 일부 변종은 암호화폐 지갑 정보도 수집합니다.
Q. TanStack은 왜 84개 악성 버전이 생겼나요?
A. 오염된 캐시를 복원한 TanStack 릴리스 워크플로가 두 번 실행됐기 때문입니다(이전 실행의 재실행 1회, 새 실행 1회). 2026년 5월 11일 19시 20분과 26분(UTC)에 약 6분 간격으로 42개 패키지에 악성 버전이 하나씩 올라가, 패키지마다 정확히 2개씩 모두 84개가 됐습니다. 외부 연구자가 게시 20~26분 만에 공개적으로 발견했습니다 (출처: TanStack 사후 분석).
Q. 내가 영향을 받았는지 어떻게 알 수 있나요?
A. 이번 공격은 진짜 패키지의 특정 버전이 오염된 것이라 이름만으로는 알 수 없습니다. 잠금 파일(package-lock.json 등)과 CI 로그에서 공지된 악성 버전을 찾으세요. TanStack 42개 패키지·84개 버전은 GitHub 권고 GHSA-g7cv-rxg3-hmpx에, @uipath·@mistralai·@cap-js·mbt·@redhat-cloud-services 버전은 Wiz 공지에 있습니다. 패키지 루트에 router_init.js나 setup.mjs 같은 흔적이 있는지도 봅니다. npm audit은 권고가 나온 악성 버전은 잡지만, 게시 직후 권고가 나오기 전의 버전은 놓칠 수 있습니다. chalk-tempalte·axois-utils처럼 소스 공개 뒤 한 공격자가 철자를 흉내 내 올린 악성 패키지는 이름으로 따로 대조합니다.

1. 공격 개요: 4월 SAP에서 시작해 TanStack·레드햇까지

샤이-훌루드 계열 공격은 2025년 9월부터 npm 공급망 공격에 여러 차례 쓰였다 (출처: 데일리시큐, 2026-05-19). 2026년 들어 이어진 TeamPCP의 작전과 그 뒤의 모방 공격 흐름은 다음과 같다(날짜는 UTC 기준).

날짜 사건 출처
2026-04-22 @bitwarden/cli 2026.4.0 오염(약 1시간 반 배포), 악성코드에 'Shai-Hulud: The Third Coming' 표기 Aikido, The Hacker News
2026-04-29 SAP 관련 npm 패키지(@cap-js/sqlite, @cap-js/postgres, @cap-js/db-service, mbt) 오염, '미니 샤이-훌루드' 이름 처음 확인 Wiz
2026-05-11~12 TanStack 42개 패키지에 악성 버전 84개(11일), 이어 UiPath·Mistral AI·OpenSearch·Guardrails AI 등 TanStack, Wiz
2026-05-12 무렵 TeamPCP가 소스 코드를 GitHub에 직접 공개 옥스시큐리티
2026-05-17 같은 공격자가 올린 악성 패키지 4종 보고(이 중 chalk-tempalte가 공개 코드 복제본, axois-utils는 디도스 봇) 옥스시큐리티
2026-06-01 '미아즈마' 변종이 @redhat-cloud-services 패키지 32개·버전 90개 오염(Wiz 갱신 표 기준) Wiz
2026-06-04 미아즈마와 연결된 추가 파동, binding.gyp로 설치 때 악성코드 실행 Wiz

주간 다운로드 약 1,200만 회인 @tanstack/react-router가 포함된 TanStack 공격은 소스 공개보다 먼저 일어난 TeamPCP의 작전이다. 공격자는 가짜 패키지를 만들지 않고 TanStack의 릴리스 파이프라인을 거쳐, 정상 이름과 정상 출처 증명(SLSA)이 붙은 악성 버전을 올렸다. Wiz는 5월 13일 갱신에서 @uipath·@mistralai npm 패키지의 악성코드는 버그 때문에 작동하지 않았다고 밝혔다. PyPI 쪽 피해 버전은 mistralai 2.4.6과 guardrails-ai 0.10.1이다.

소스 공개 뒤에는 모방이 이어졌다. 옥스시큐리티는 공개 직후 해킹 포럼 BreachForums에 '공급망 공격 대회' 글이 올라왔다고 짚었다. 이어 모방범 패키지 chalk-tempalte를 분석해 코드가 난독화되지 않은 상태였고 유출된 원본과 거의 같았다는 점에서, 기존 TeamPCP가 아닌 모방 공격자일 가능성이 높다고 봤다 (출처: 데일리시큐, 2026-05-19). chalk-tempalte와 같은 npm 계정으로 올라온 나머지 3종은 저마다 다른 정보 탈취 악성코드였다(옥스시큐리티, 2026-05-17). 6월 1일 레드햇 패키지를 오염시킨 미아즈마도 공개된 코드를 바탕으로 한 것으로 보이는 변종이다(Wiz). Wiz는 레드햇 직원 한 명의 GitHub 계정이 탈취된 것으로 보인다고 밝혔고, TeamPCP의 소행인지 모방범인지는 단정할 수 없다고 했다. 6월 4일에는 binding.gyp로 설치 때 악성코드를 실행하는 추가 파동이 나왔고, Wiz는 이를 미아즈마 캠페인과 연결했다.

즉, 5월의 확산은 TeamPCP가 CI/CD를 노린 공격을 이어 간 결과이고, 5월 12일 무렵 소스가 공개되면서 모방 공격까지 더해졌다. 도구의 진입 장벽이 내려가면 공격자 풀이 넓어지고, 변종 생성 속도도 빨라진다.


2. 전파·탈취 체인: 설치하는 순간 무슨 일이 벌어지나

공격 흐름을 단계별로 분해하면 아래와 같다.

단계 행위 주요 기술
1. 배포 경로 탈취 유지관리자 계정·CI/CD(OIDC) 탈취 → 정상 패키지에 악성 버전 게시 (모방범 일부는 철자가 비슷한 가짜 패키지) Actions 캐시 오염, 러너 메모리의 OIDC 토큰 탈취
2. 설치 트리거 npm install 등 설치 단계에서 스크립트 자동 실행 preinstall(setup.mjs), git 의존성의 prepare, binding.gyp
3. 자격증명 수집 환경변수·설정파일·메모리에서 토큰 수집 ~/.aws/credentials, .env, ~/.npmrc
4. 탈취·유출 공격자 서버·Session 메신저 네트워크로 전송, 공개 깃허브 저장소에 업로드 Session 네트워크, GitHub GraphQL API
5. 측면 이동 탈취한 토큰으로 클라우드 콘솔·깃허브 저장소·CI/CD 접근 AWS CLI, GitHub API
6. 지속 유지 개발 도구 설정 폴더(.vscode/tasks.json, AI 코딩 도구 설정)의 실행 훅, OS 서비스(gh-token-monitor) 설치, Actions 워크플로 주입 LaunchAgent·systemd, .github/workflows/

TanStack 공격은 철자를 흉내 낸 도메인(git-tanstack[.]com), Session 메신저 네트워크, 탈취한 토큰으로 만든 GitHub 저장소(데드드롭) 세 경로로 데이터를 빼냈다. Session 경로는 종단간 암호화라 도메인·IP 차단 말고는 네트워크에서 막을 방법이 거의 없다(TanStack 사후 분석).

가장 위험한 구간은 4→5 단계다. GitHub 토큰이 탈취되면 공격자는 리포지토리에 접근해 또 다른 악성 코드를 커밋하거나, OIDC 기반으로 AWS 임시 자격증명을 발급받을 수 있다. 토큰 하나가 클라우드 계정 침해로 이어질 수 있는 구조다.

옥스시큐리티가 보고한 같은 공격자의 악성 패키지 가운데 axois-utils는 정보 탈취에 더해 감염 시스템을 디도스(DDoS) 공격에 동원하는 봇('phantom bot') 기능까지 담고 있었다 (출처: 데일리시큐 2026-05-19).


3. 개발팀·DevOps가 입는 실제 피해

이 공격이 단순한 정보탈취와 다른 이유는 개발 파이프라인 자체가 공격 경로가 된다는 점이다.

피해 유형 구체적 시나리오
소스코드 유출 깃허브 리포지토리 전체 clone 후 외부 전송
공급망 오염 npm 배포 토큰 탈취 → 정상 패키지에 악성 버전 추가 배포
클라우드 인프라 침해 AWS/GCP/Azure 키로 S3 덤프, EC2 신규 인스턴스 생성, IAM 백도어
CI/CD 무결성 훼손 깃허브 Actions 워크플로우 변조 → 이후 빌드 전체 오염
컨테이너 레지스트리 침해 Docker Hub·ECR 자격증명 탈취 → 악성 컨테이너 이미지 푸시

CI/CD가 오염되면 "코드를 고쳐도 배포 과정에서 다시 감염"되는 악순환이 생긴다. 이 상태에서는 패키지 제거만으로 사고가 해결되지 않는다. 토큰 전체를 교체하고 워크플로우 히스토리를 감사해야 한다.

다만 순서가 중요하다. TanStack 공격의 악성코드는 개발자 PC에 gh-token-monitor라는 서비스(macOS LaunchAgent, 리눅스 systemd)를 심고, GitHub 토큰이 폐기되면 홈 디렉터리를 지우는 명령(rm -rf ~/)을 실행하려 한다. 이 서비스는 훔친 GitHub 토큰이 일정 조건(쓰기 권한·조직 구성원 등)을 만족할 때 설치되고, 시작 뒤 24시간이 지나면 스스로 종료된다. 하지만 서비스 파일이 남아 재부팅 때 다시 실행될 수 있다. Wiz는 GitHub 토큰을 폐기하기 전에 이 서비스부터 찾아 지우라고 권고했다. 개발 도구 설정 폴더(.vscode/, AI 코딩 도구 설정 폴더)에 심은 실행 훅과 setup.mjs·router_runtime.js 같은 파일은 npm uninstall 뒤에도 남으므로 함께 찾아 지운다.

📌 개발팀이 가장 놓치는 지점
npm audit은 레지스트리의 권고(advisory) 데이터로 판정하므로, CVE가 없는 악성코드 권고도 결과에 나온다. TanStack 악성 버전에도 2026년 5월 12일 GitHub 권고 GHSA-g7cv-rxg3-hmpx(CVE-2026-45321, 심각도 critical)가 나왔다. 한계는 시간이다. 권고가 나오기 전, 막 게시된 악성 버전은 audit이 잡지 못한다. TanStack 악성 버전은 게시 뒤 약 1시간~1시간 43분 만에 사용 중단(deprecate) 표시가 붙었고, 약 2시간 53분~4시간 35분 만에 레지스트리에서 지워졌다(TanStack 사후 분석). 그 사이에 악성 버전이 설치되는 것을 막으려면 잠금 파일 고정과 설치 스크립트 통제가 함께 필요하다.


4. 의존성 점검 단계: 지금 당장 할 수 있는 것

사고가 발생한 뒤에 하는 게 아니라, 주기적으로 해야 하는 작업이다.

Step 1. 잠금 파일과 CI 로그에서 공지된 악성 버전 찾기

이번 공격에서는 진짜 패키지의 특정 버전이 오염됐다. 이름은 정상이므로 버전으로 확인해야 한다. package-lock.json·pnpm-lock.yaml·yarn.lock과 CI 설치 로그에서 아래 공지에 나온 버전을 찾는다.

  • TanStack 42개 패키지·84개 버전: GitHub 권고 GHSA-g7cv-rxg3-hmpx
  • @uipath·@mistralai·@cap-js·mbt 등: Wiz 공지(2026-05-11)의 피해 패키지 표
  • @redhat-cloud-services: Wiz 미아즈마 공지(2026-06-01)
  • 6월 4일 binding.gyp를 쓴 추가 파동: 미아즈마 공지의 6월 4일 갱신에 안내된 Wiz 별도 위협 권고
  • PyPI: mistralai 2.4.6, guardrails-ai 0.10.1

패키지 루트에 router_init.js나 setup.mjs 같은 흔적이 있는지도 본다. TanStack 악성 버전은 2026년 5월 11일(UTC) 공개 레지스트리에서 삭제돼 지금은 받을 수 없으므로, 기본 확인은 잠금 파일과 CI 로그의 버전 대조로 한다. 아래 npm pack 확인은 사내 미러(Artifactory·Nexus 등)나 캐시에 그 버전이 남아 있을 때만 쓴다. npm pack은 설치 스크립트를 실행하지 않고 압축 파일만 받아 온다.

# 사내 미러·캐시에 남은 버전을 설치 스크립트 실행 없이 받아 확인(GHSA-g7cv-rxg3-hmpx 안내)
npm pack @tanstack/<패키지명>@<버전>
tar -xzf *.tgz
grep -A3 optionalDependencies package/package.json
ls -la package/router_init.js

옥스시큐리티가 2026년 5월 신고한 아래 4종은 같은 공격자가 올린 악성 패키지다(chalk-tempalte는 공개된 샤이-훌루드 코드의 복제본). 이 이름이 잠금 파일에 남아 있다면 즉시 격리한다.

  • chalk-tempalte
  • @deadcode09284814/axios-util
  • axois-utils
  • color-style-utils

Step 2. 설치 단계 스크립트 감사(preinstall·install·postinstall·prepare)

# 의존성 트리 전체(scope 패키지·하위 의존성 포함)에서 설치 스크립트가 있는 패키지 출력
npm query ':attr(scripts, [preinstall]), :attr(scripts, [install]), :attr(scripts, [postinstall])' \
  | jq -r '.[] | "\(.name)@\(.version)"' | sort -u

# git·GitHub 주소를 가리키는 의존성 찾기(git 의존성은 설치 때 prepare 스크립트가 실행될 수 있다)
grep -nE '"(github:|git\+)' package-lock.json

npm 11.21 이상과 npm 12에서는 npm approve-scripts --allow-scripts-pending으로 아직 검토하지 않은 설치 스크립트를 가진 패키지 목록을 볼 수 있다(npm 12는 이런 스크립트를 기본으로 실행하지 않는다). 이 명령이 없는 이전 버전이라면 위 npm query 결과로 대신한다. 스크립트에 curl, wget, fetch 같은 외부 통신이나 Bun 같은 런타임 다운로드가 있으면 즉시 검토 대상이다.

Step 3. 지속 장치부터 지우고 환경변수·.env 토큰 정리

토큰을 바꾸기 전에 개발자 PC에서 gh-token-monitor 서비스(macOS: ~/Library/LaunchAgents/com.user.gh-token-monitor.plist, 리눅스: ~/.config/systemd/user/gh-token-monitor.service)와 개발 도구 설정 폴더의 낯선 실행 훅·setup.mjs·router_runtime.js부터 찾아 지운다. 공격 직후 C&C로 전송된 데이터는 복구가 불가능하다. 하지만 현재 설정 파일에 불필요하게 넓은 권한의 토큰이 남아 있는지 확인하고, 최소 권한 토큰으로 교체한다.

Step 4. 깃허브 Actions 워크플로우 무결성 검증

.github/workflows/ 디렉토리의 최근 변경 이력을 git log로 확인한다. 본인이 수정하지 않은 변경이 있다면 침해 신호다.


5. CI/CD 토큰 보호: 설계 단계부터 달라져야 하는 것

이번 사고를 계기로 점검해야 할 CI/CD 보안 설계 원칙은 세 가지다.

원칙 1. 장기 토큰 → OIDC 임시 자격증명으로 전환

AWS_ACCESS_KEY_ID 같은 장기 자격증명을 GitHub Secrets에 저장하는 방식은 구조적으로 취약하다. GitHub Actions ↔ AWS OIDC 연동을 사용하면 각 워크플로우 실행마다 임시 자격증명이 발급되고, 탈취돼도 짧은 시간 안에 만료된다(AWS STS의 웹 ID 임시 자격증명은 기본 1시간, 역할 설정에 따라 최대 12시간).

다만 OIDC도 만능은 아니다. TanStack과 미아즈마 공격은 OIDC 자체를 악용해 패키지를 게시했다. id-token: write 권한은 게시 전용 작업에만 주고, 신뢰된 게시자(trusted publishing)는 보호된 브랜치·환경에서만 허용하며, pull_request_target 워크플로에서 포크의 코드를 실행하지 않는다.

원칙 2. 최소 권한(Least Privilege) 엄격 적용

빌드 토큰이 S3 버킷 전체 읽기 권한을 갖고 있을 필요가 없다. GitHub 토큰 권한은 permissions: 블록으로 워크플로우 단위로 명시적 제한을 걸어야 한다.

permissions:
  contents: read   # 소스 읽기만
  packages: write  # 패키지 배포만
  # id-token: write  # OIDC가 필요한 경우에만 활성화

원칙 3. 의존성 고정(Lock File) + 해시 검증

package-lock.json, poetry.lock, 해시를 넣은 requirements.txt(pip install --require-hashes로 설치) 또는 표준 잠금 파일 pylock.toml(PEP 751, 2025년 3월 확정)을 반드시 커밋하고, CI에서 npm ci(install이 아닌 ci)로 설치한다. 이렇게 하면 버전 범위(^, ~)에 해당하는 악성 최신 버전이 자동 설치되는 상황을 막을 수 있다.


6. 자가점검 체크리스트

현재 개발 환경에 아래 항목을 대조해보자.

항목 점검 내용 상태
공지된 악성 버전 잠금 파일·CI 로그에 공지된 악성 버전(GHSA-g7cv-rxg3-hmpx 등) 없음 ☐
패키지 이름 오타 package.json 의존성에 유사 오타 패키지 없음 ☐
설치 단계 스크립트 preinstall·install·postinstall·prepare에 외부 통신·다운로드 없음, 검토 안 된 스크립트 없음 ☐
지속 장치 gh-token-monitor 서비스·개발 도구 설정 폴더의 낯선 실행 훅 없음 (토큰 폐기 전에 확인) ☐
장기 토큰 저장 AWS 장기 키를 Secrets에 저장하지 않음 (OIDC 전환) ☐
GitHub 토큰 권한 워크플로우별 permissions: 최소 권한 명시, id-token: write는 게시 전용 작업에만 ☐
Lock File 사용 npm ci / pip install --require-hashes로 고정 설치 ☐
워크플로우 변경 이력 .github/workflows/ 최근 변경 본인 확인 ☐
.env 토큰 유효성 불필요한 고권한 토큰 만료·재발급 (지속 장치 확인 뒤) ☐
의존성 감사 주기 npm audit / pip-audit 주간 실행 CI 통합 ☐

10개 중 절반 이상이 ☐이면 의존성 파이프라인 점검이 시급하다. 잠금 파일 점검부터 시작하고, 고권한 토큰 교체는 지속 장치를 확인한 뒤에 한다.


7. Bitwarden 사건과 무엇이 다른가

기존에 다룬 Bitwarden CLI 사건(관련 글 참조)도 같은 계열의 웜이었다. 2026년 4월 22일 약 1시간 반 동안 배포된 @bitwarden/cli 2026.4.0의 악성코드에는 'Shai-Hulud: The Third Coming'이라는 이름이 박혀 있었고, 탈취한 GitHub 토큰으로 저장소에 Actions 워크플로를 주입해 CI/CD 비밀값을 빼내는 전파 기능도 있었다(Aikido, The Hacker News 보도). Wiz는 이 사건도 TeamPCP의 작전으로 평가했다.

차이는 규모와 경로다. Bitwarden은 패키지 1개의 CI 게시 경로가 뚫린 사건이고, 미니 샤이-훌루드 계열 공격은 4~6월에 SAP·TanStack·UiPath·Mistral AI를 거쳐 레드햇(공개된 코드를 바탕으로 한 것으로 보이는 변종)까지 여러 생태계로 번졌다. 탈취한 npm 토큰뿐 아니라 GitHub Actions OIDC 토큰으로도 피해자가 관리하는 다른 패키지에 악성 버전을 다시 게시했기 때문에, 한 명의 개발자 PC나 CI 러너에서 시작된 감염이 그 사람이 관리하는 패키지 전체로 번질 수 있다.

기업 단위로 보면, CI/CD 서버가 감염되면 그 서버가 배포한 모든 패키지가 잠재적 감염원이 된다. 이것이 '웜(worm)'이라고 부르는 이유다.

빌드마다 SBOM과 잠금 파일을 남겨 두면 영향받은 버전이 들어간 빌드를 빠르게 좁힐 수 있다. 다만 이번 악성코드는 설치하는 순간 개발자 PC와 CI 러너에서 실행됐다. TanStack은 5월 11일에 영향받은 버전을 설치한 호스트를 침해된 것으로 간주하라고 안내했다. 어느 PC와 러너가 설치했는지는 잠금 파일과 CI 로그 같은 설치 기록으로 따로 찾아야 한다. 공급망 보안 체계 전반을 점검하고 싶다면 공급망 보안 체크리스트를 함께 참고하자.

업무 계정에 연결한 서드파티 AI 도구가 침해의 출발점이 된 사례는 Vercel 사고로 본 AI 도구 공급망 리스크에서 다뤘다.


지금 해야 할 한 가지를 고르라면

npm install 한 번이 남의 코드를 내 PC와 CI에서 실행하는 일이라는 점은 쉽게 잊힌다.

preinstall·postinstall 같은 설치 스크립트는 설계 의도상 빌드 자동화를 위한 기능이지만, 공격자 입장에선 설치만으로 코드를 실행할 수 있는 진입점이다. 설치 스크립트를 기본으로 막고 꼭 필요한 패키지만 허용하도록 내부 정책을 바꾸는 것이 지금 당장 할 수 있는 가장 효과적인 한 가지다. TanStack 악성 버전 권고(GHSA-g7cv-rxg3-hmpx)도 임시 방어책으로 설치 스크립트를 끄는 설정(npm config set ignore-scripts true)을 권했다.

npm 12(2026년 7월 출시, 10월 현재 최신 12.2.0)부터는 이것이 기본 동작이다. 의존성의 설치 스크립트(preinstall·install·postinstall과 git 같은 비레지스트리 의존성의 prepare)는 package.json의 allowScripts에 허용한 패키지만 실행되고, git 의존성도 기본으로 받지 않는다(allow-git 기본값 none). 그래서 이번 TanStack식 git 의존성 경로는 기본 설정에서 막힌다. 반면 npm 11 이하는 allowScripts를 적어 둬도 설치 스크립트를 그대로 실행한다(최근 11.x는 검토하지 않은 목록을 경고로만 보여 준다). 2026년 10월 현재 Node.js에 기본으로 들어 있는 npm은 아직 12가 아니다(Node.js 22는 npm 10, 24·26은 npm 11). npm 11 이하를 쓴다면 ignore-scripts(npm config set ignore-scripts true 또는 --ignore-scripts)를 켜 두고, 가능하면 npm 12로 올린다.

의존성 취약점은 npm audit·pip-audit 같은 의존성 분석(SCA) 도구와 잠금 파일(lockfile) 고정으로 먼저 점검한다.


다음 행동

개발자·DevOps 팀이라면
4장의 Step 1~3을 오늘 순서대로 진행하세요. 잠금 파일과 CI 로그에서 공지된 악성 버전을 찾고, 설치 단계 스크립트를 감사한 뒤, 지속 장치(gh-token-monitor 등)를 지우고 나서 토큰을 교체합니다. 그다음 npm 12로 올리거나 ignore-scripts를 켜서 설치 스크립트를 기본으로 막으세요. 보안책임자·CTO라면
영향받은 버전을 설치한 흔적이 있거나 침해가 의심되면, 보안 전문 기업 SENTRIX의 침해사고 대응으로 원인 분석(포렌식)·악성코드 분석·피해 범위 산정·재발 방지 대책을 요청할 수 있습니다. 탈취된 토큰이 닿는 AWS·Azure·GCP 보안 구성 점검은 보안 아키텍처 리뷰로 상담할 수 있습니다.
👉 진단 문의하기

참고 자료


관련 글

침해가 의심되시나요?

긴급 보안 대응이 필요하면 즉시 연락하세요.