NPM에 8만6천 회 이상 다운로드된 악성 패키지 대량 유포
(arstechnica.com)- NPM 저장소에서 100개 이상 자격 증명 탈취용 악성 패키지가 8월 이후 탐지되지 않은 채 업로드되어, 총 8만6천 회 이상 다운로드된 사실이 확인됨
 - 보안업체 Koi는 ‘PhantomRaven’으로 명명한 공격 캠페인이 NPM의 Remote Dynamic Dependencies(RDD) 기능을 악용해 126개의 악성 패키지를 배포했다고 보고
 - RDD는 패키지가 신뢰되지 않은 도메인에서 동적으로 의존성 코드를 내려받을 수 있게 하는 구조로, 정적 분석 도구에서는 탐지되지 않음
 - 공격자는 이 기능을 이용해 HTTP 연결을 통한 악성 코드 다운로드를 수행했으며, 패키지 메타데이터에는 “0 Dependencies”로 표시되어 개발자와 보안 스캐너가 인식하지 못함
 - 이러한 구조적 취약점은 NPM 생태계의 보안 관리 한계와 자동 설치 메커니즘의 위험성을 드러냄
 
NPM 저장소 내 악성 패키지 확산
- 공격자들이 NPM 코드 저장소의 구조적 약점을 이용해 8월 이후 100개 이상의 자격 증명 탈취용 패키지를 업로드
- 대부분의 패키지가 탐지되지 않은 채 배포되었으며, 누적 다운로드 수는 86,000회 이상
 
 - 보안업체 Koi는 이 공격을 PhantomRaven 캠페인으로 명명하고, NPM의 특정 기능이 악용되었다고 분석
- Koi에 따르면 126개의 악성 패키지 중 약 80개가 기사 작성 시점에도 여전히 NPM에 남아 있었음
 
 
Remote Dynamic Dependencies(RDD)의 취약 구조
- RDD는 패키지가 외부 웹사이트에서 의존성 코드를 동적으로 다운로드하도록 허용하는 기능
- 일반적으로 의존성은 NPM의 신뢰된 인프라에서 내려받지만, RDD는 HTTP 등 암호화되지 않은 연결을 통한 다운로드도 허용
 
 - PhantomRaven 공격자는 이 기능을 이용해 악성 URL(예: 
http://packages.storeartifact.com/npm/unused-imports)에서 코드를 내려받도록 설정- 이러한 의존성은 개발자와 보안 스캐너에 보이지 않으며, 패키지 정보에는 “0 Dependencies”로 표시됨
 
 - NPM의 자동 설치 기능으로 인해 이러한 ‘보이지 않는’ 의존성 코드가 자동 실행되는 구조
 
보안 도구의 탐지 한계
- Koi의 Oren Yomtov는 “PhantomRaven은 기존 보안 도구의 사각지대를 정교하게 악용한 사례”라고 언급
- RDD는 정적 분석 도구에서 탐지되지 않음
 
 - 이로 인해 공격자는 보안 검증을 우회하며 악성 코드를 배포할 수 있었음
 
추가적 취약 요인
- Koi는 RDD로 내려받은 의존성이 매 설치 시마다 공격자 서버에서 새로 다운로드된다고 설명
- 캐시나 버전 관리가 없어, 동일 패키지라도 설치 시점마다 다른 악성 코드가 주입될 가능성 존재
 
 - 이러한 동적 다운로드 구조는 패키지 무결성 검증을 어렵게 함
 
NPM의 구조 및 배경
- NPM은 JavaScript용 패키지 관리자로, GitHub 자회사인 npm, Inc. 가 관리
- Node.js의 기본 패키지 관리자이며, 명령줄 클라이언트와 npm registry로 구성
 - registry에는 공개 및 유료 비공개 패키지가 저장되며, 웹사이트를 통해 검색 가능
 
 - 이번 사건은 NPM의 자동 의존성 관리 구조가 공격에 악용될 수 있음을 보여주는 사례로 지적됨
 
기타 언급
- 기사 말미에서는 불필요한 JavaScript 실행을 차단해야 한다는 의견이 언급됨
- 그러나 이번 공격은 필수 JavaScript 코드조차 악용된 사례로 지적됨
 
 
Hacker News 의견
- 
요즘 나는
npm명령어를 Docker 컨테이너 안에서 실행하도록 alias를 걸어둠
이렇게 하면 내 환경 변수를 노출하지 않고, 현재 디렉토리 외부 파일에도 접근하지 않으며,.bashrc같은 설정 파일에도 접근하지 못하게 됨
참고: Run tools inside Docker- 그건 너무 과한 샌드박싱 같음. 어차피 
npm은 바로 실행될 임의 코드를 다운로드하니까
대신pnpm을 추천함. 기본적으로 lifecycle 스크립트를 실행하지 않고, 허용할 스크립트를 화이트리스트로 지정할 수 있음 - post-install 스크립트를 악마화하는 건 잘못된 안전 환상을 줄 뿐임
진짜 보호를 원한다면 설치뿐 아니라 실행 전체를 샌드박스 안에서 돌려야 함
지금처럼 post-install만 막는 건 절반짜리 대책에 불과함. 공급망 공격은 점점 더 위험해지고 있음 - 공격 벡터는 너무 많음. 악의적인 의도를 가진다면 인기 있는 플러그인이나 LSP 이름을 오타 스쿼팅해서 에디터 실행 시 자동으로 코드가 실행되게 만들 수 있음
neovim이나vscode가 감염되면 이미 사용자 권한으로 충분히 위험한 일들을 할 수 있음 - 나는 sandbox-run을 사용함
단순 alias는node/npm에는 통하지만, 다른 프로그램들에는 적용하기 어려움. 컨테이너에 필요한 리소스를 마운트해야 하니까 - 그래도 결국 악성 패키지를 다운로드할 수 있지 않음? 의존성 자체가 감염되어 있을 수도 있음
 
 - 그건 너무 과한 샌드박싱 같음. 어차피 
 - 
예전부터 궁금했음. 왜 사람들은 아무렇지 않게
npm을 시스템에서 돌리는지
make처럼 재현 가능한 빌드에 익숙한 입장에서는,npm이 매번 다른 걸 다운로드하고 다른 결과를 내는 게 충격이었음
CSS 생성조차 npm 의존성으로 묶는 게 이상했음. 그래서 Docker 안에 npm 환경을 통째로 고정(freeze) 시켜봤지만, 결국 지는 싸움 같음- 요즘 모든 패키지 매니저가 그런 식으로 동작함. 
maven,nuget,pip,npm모두 마찬가지임
과거처럼 배포판 패키지 매니저에 의존하면 지금 같은 빠른 생태계는 불가능했을 것임
다만 보안이 강화된 새 패키지 매니저들이 등장하고 있음. 이유를 이해하지 못한 채 수단만 비난하는 건 옳지 않음 - 프론트엔드 개발은 마치 “믿어줘 브로”식 와일드 웨스트 같음. 브라우저 진화 과정상 어쩔 수 없이 덕트테이프처럼 이어붙인 느낌임
 - 
npm을 Docker로 고정했다면, 매번 의존성 업데이트 후 그 환경을 검증했는지 묻고 싶음
사실npm과pnpm은 이미 기본적으로 lock 파일로 의존성을 고정함 - “
npm install thing”이 너무 쉽고 싸서 생긴 문제임
많은 오픈소스가 품질보다 이력서용 코드로 채워지고, 결국 거대 기업의 광고 추적기나 지갑 앱 같은 걸 만드는 데 쓰임 
 - 요즘 모든 패키지 매니저가 그런 식으로 동작함. 
 - 
npm install은 단순히 패키지를 다운로드하는 게 아니라 코드를 실행함
package.json의 preinstall, install, postinstall 훅이 실제로 실행됨
합법적으로 설치 과정에서 임의 명령을 실행해야 할 이유가 뭘까?
관련 보고서: PhantomRaven npm malware
또 다른 사례: Socket.dev 블로그- 사실 이런 구조는 오래된 패키지 매니저(DEB, RPM)에도 있었음
예를 들어 리눅스 커널 패키지는 설치 후 initramfs 재생성, GRUB 업데이트 등을 위해 post-install 스크립트를 실행함
대부분의 DEB/RPM 패키지에 이런 스크립트가 포함되어 있음. 즉, 설계 자체의 문제임 - 문제는 
npm은 아무나 패키지를 올릴 수 있다는 점임
리눅스 배포판은 신뢰할 수 있는 메인테이너 체계가 있고, PGP 기반의 루트 트러스트를 직접 구축하기도 함
반면npm,pip,rubygems,cargo등은 사실상 “curl | bash”의 세련된 버전일 뿐임 - 예를 들어 Mediasoup 프로젝트는 C++로 작성된 스트리밍 라이브러리인데, 설치 시 소스를 직접 컴파일함
유지보수 부담을 줄이기 위해 이런 post-install 빌드가 필요했음 - 
Swift Package Manager도 
Package.swift파일을 실제로 실행함
다만 강하게 샌드박싱되어 있어서 악용은 어렵다고 들음
참고: SwiftPM 문서, PackageDescription - 참고로 
pnpm v10은 기본적으로 모든 lifecycle 스크립트를 비활성화하고, 사용자가 직접 허용해야 함
관련 논의 
 - 사실 이런 구조는 오래된 패키지 매니저(DEB, RPM)에도 있었음
 - 
최근
npm공격들을 보면, 이제는npm으로 개발하는 게 안전한가 싶음
React 프로젝트를 시작할 때마다 수백 개의 패키지가 깔리는데, 뭘 하는지도 모름
백엔드에서는 필요한 패키지만 명시적으로 설치하지만, 프론트엔드는 취약점의 판도라 상자 같음- 사실 모든 언어 생태계가 비슷함. 단지 
npm이 제일 크고 뉴스에 많이 나올 뿐임 - Rust의 
jj를 설치했더니 470개, Python의wan2gp는 211개 패키지가 깔렸음. 다 거기서 거기임 - JavaScript 생태계는 구조적으로 공격에 취약함
xz사건처럼, 각 의존성이 무작위 개인에게 달려 있고, 그들이 사회공학 공격에 당하지 않을 거라 믿어야 함 - 의존성이 적을수록 좋음. 0개면 완벽임. 그게 진짜 승리임
 - 참고로 
PyPI도 안전하지 않음. GitHub Actions 해킹으로 정상 패키지에 악성 코드가 삽입된 사례도 있음 
 - 사실 모든 언어 생태계가 비슷함. 단지 
 - 
Angular, Vue 같은 프레임워크로 개발할 때마다 불안함
node_modules안의 수천 개 의존성을 보면 재앙의 예고처럼 느껴짐
오픈소스 개발자 한 명이 피싱당하면 바로 감염될 수도 있음
JavaScript 생태계는 근본적으로 깨져 있음. 오타 한 번으로 공급망 공격에 노출됨
NuGet이나 Maven도 가능은 하지만, 그쪽은 표준 라이브러리가 커서 의존성이 적고 통제감이 있음- Go는 패키지 이름 대신 repo URL을 사용해서 오타 스쿼팅을 줄임
완벽하진 않지만 그래도 한 단계 나음 - Deno는 이런 문제를 해결함. Node.js / npm의 구조적 문제임
 
 - Go는 패키지 이름 대신 repo URL을 사용해서 오타 스쿼팅을 줄임
 - 
86,000회 다운로드 중 대부분은 실제 사용자가 아니라 자동화된 스캐너나 봇일 가능성이 큼
새 버전을 올리면 하루이틀 만에 수백 번 다운로드되지만, 실제 사람은 아닐 수도 있음
즉, 감염된 사용자는 거의 없을 수도 있음- 나도 라이브러리를 올렸을 때 초반엔 주당 300회, 이후엔 100회 정도 다운로드됐음
AI 챗봇이 환각으로 만들어낸 패키지 이름을 노린 공격도 많음. 단순한 통계 이상임 - 혹은 누군가의 좀비 CI가 계속 다운로드하는 걸 수도 있음
 - 하지만 LLM이 만들어낸 가짜 패키지 이름을 노린 공격이라면, 실제로 많은 개발자가 감염됐을 수도 있음
 
 - 나도 라이브러리를 올렸을 때 초반엔 주당 300회, 이후엔 100회 정도 다운로드됐음
 - 
더 자세한 공격 설명은 BleepingComputer 기사 참고
 - 
npm install중 HTTP URL을 의존성으로 사용하는 패키지를 탐지하거나 필터링할 방법이 있을까 궁금함
요청자별로 다른 페이로드를 보낼 수 있어서 일반적인 스캐너로는 탐지가 어려움 - 
취미 개발자로서 이런 공급망 공격에 대비하려면 어떻게 해야 할지 고민임
유명한 튜토리얼을 따라가며 의존성을 설치하다 보면, 어느새 보안에 무심해짐
내 홈랩에도 여러 서비스를 돌리는데, 혹시라도 봇이 침투할까 걱정임. 어디서부터 시작해야 할까?- 컨테이너나 VM 안에서 서비스를 분리해 돌리면 피해를 격리할 수 있음
완벽한 보장은 아니지만, 전체 서버가 뚫리는 것보단 훨씬 낫음 - 모든 의존성을 잠재적 보안 리스크로 보고, 꼭 필요할 때만 사용해야 함
필요한 코드만 직접 복사해 쓰는 것도 좋은 학습이자 안전한 방법임 - 인기 있고 1년 이상 된 릴리스를 사용하는 게 안전함. 문제가 있었다면 이미 발견됐을 확률이 높음
 - FreeBSD처럼 시스템 패키지 매니저를 사용하는 OS도 있음
이런 구조라면 수백만 사용자가 직접 검증할 필요 없이 배포판 수준에서 신뢰성 확보가 가능함 - 주간 다운로드 100만 회 이상, 의존성이 없는 패키지를 선호함
예: Hono, Zod
나는 최근 Bun으로 전환했는데, 기본적으로 DB 드라이버나 S3 클라이언트 같은 게 내장되어 있어 추가 다운로드가 줄어듦 
 - 컨테이너나 VM 안에서 서비스를 분리해 돌리면 피해를 격리할 수 있음
 - 
lifecycle 훅에서 의존성을 가져오는 구조는 언제든 공격 전환점이 될 수 있음
지금은 정상이라도, 나중에 소유자가 해킹당하거나 마음을 바꾸면 악성 코드로 바뀔 수 있음
이런 형태의 설치 훅은 결국 지속 불가능한 설계임