- 오픈소스 패키지 @ctrl/tinycolor가 포함된 다수의 npm 패키지가 악성 버전에 감염되는 사건이 발생, 원인은 협업 저장소의 GitHub Actions 워크플로우를 통한 npm 토큰 탈취였음
- 공격자는 broad 권한의 npm 토큰을 이용해 약 20개의 패키지에 악성 코드를 배포했으며, 이 중 @ctrl/tinycolor는 주간 다운로드 수가 200만 회에 달해 영향력이 컸음
- 감염된 버전은 postinstall 단계에서 악성 페이로드를 실행했으며, GitHub 및 npm 보안팀이 빠르게 대응해 삭제 및 정리 조치가 이루어졌음
- 작성자는 재발 방지를 위해 Trusted Publishing(OIDC) 전환, 토큰 권한 최소화, 2FA 의무화, pnpm 기능 활용 등 강화된 보안 계획을 마련
- 이번 사건은 소프트웨어 공급망 보안의 취약성을 보여주며, npm 생태계 전반의 보안 기능 개선과 보안 관행 변화의 필요성을 드러낸 사례임
TL;DR
- 악성 GitHub Actions 워크플로우가 공유 저장소에 푸시되어 npm 토큰을 탈취한 사건
- 해당 토큰으로 공격자는 20개 패키지의 악성 버전을 배포했고, 그중 @ctrl/tinycolor는 다운로드량이 많아 파급력이 컸음
- 개인 계정이나 레포지토리는 직접 침해되지 않았고, 피싱이나 로컬 악성코드 설치도 없었음
- GitHub/npm 보안팀의 신속 대응으로 악성 버전이 제거되었고, 이후 깨끗한 버전을 다시 배포해 캐시를 정리했음
사건 발견 경위 (How I Found Out)
- 9월 15일 오후, 커뮤니티 멤버 Wes Todd가 Bluesky DM을 통해 문제를 알려주었음
- 이미 GitHub/npm 보안팀은 영향을 받은 패키지 목록을 정리하고 삭제 조치를 시작
- 초기 단서로 'Shai-Hulud'라는 악성 브랜치명이 공유되었으며, 이는 Dune 세계관의 샌드웜 명칭에서 따온 것임
실제 발생한 일 (What Actually Happened)
- 오래전 협업한 angulartics2 저장소에 여전히 admin 권한을 가진 협력자가 있었음
- 해당 저장소에 보관된 npm 토큰이 악성 GitHub Actions 워크플로우에 의해 탈취됨
- 공격자는 이 토큰으로 @ctrl/tinycolor 포함 약 20개 패키지를 배포했음
- GitHub/npm 보안팀이 빠르게 악성 버전을 삭제했고, 작성자가 신뢰할 수 있는 새 버전을 다시 배포했음
영향 (Impact)
- 악성 버전을 설치할 경우 postinstall 스크립트가 실행되어 보안 위협 발생
- 영향받은 사용자들은 StepSecurity의 즉각적인 대응 지침을 참고할 것을 권장함
배포 환경 및 대응 계획 (Publishing Setup & Interim Plan)
- 기존에는 semantic-release + GitHub Actions 조합으로 자동 배포를 진행
- npm provenance 기능을 활용했지만, 유효한 토큰을 가진 공격자를 막을 수는 없었음
- 향후에는 Trusted Publishing(OIDC) 도입으로 정적 토큰을 제거할 계획
- 현재는 모든 토큰을 폐기하고 2FA 필수화, granular 권한의 토큰만 허용, pnpm의 minimumReleaseAge 기능 검토 등 추가적인 보안 조치를 적용 중
이상적 개선안 (Publishing Wishlist)
- npm 계정 단위에서 OIDC 기반 Trusted Publishing 강제 옵션 제공 필요
- provenance 누락 시 배포 차단 기능, semantic-release와 OIDC의 완전한 통합 지원 필요
- GitHub UI에서 2FA 기반 수동 승인 배포 기능 제공 희망
- Pro 구독 없이도 GitHub Environments 수준의 보호 기능을 활용할 수 있어야 함
- npm 패키지 페이지에서 postinstall 스크립트 여부 표시 및 삭제된 버전의 사유 공개 필요