
npm install 한 번에 수백 개 패키지가 딸려 온다. 그중 하나가 탈취되면 그 코드는 내 빌드 서버에서 실행되고, 환경 변수와 토큰에 접근한다. 2026년에도 이런 사고가 반복해서 일어났다. 이 글은 실제로 벌어진 공격의 형태를 보고, 락파일·설치 명령·업데이트 정책 세 축으로 방어를 정리한다. 도구를 새로 사는 이야기가 아니라 지금 설정을 바꾸는 이야기다.
2026년에 실제로 일어난 일들
세 건만 봐도 공격의 성격이 드러난다.
2026년 3월, 다운로드 수가 가장 많은 축에 드는 axios가 인증 정보 탈취를 통해 침해됐다. 5월 14일에는 주당 1,000만 회 넘게 내려받는 IPC 라이브러리 node-ipc의 악성 버전 세 개가 동시에 npm 레지스트리에 올라왔다. 8월 4일에는 keyv와 cacheable에서 시작된 자기 전파형 웜이 악성 버전을 퍼뜨렸고, 영향받은 패키지 버전이 2,236개까지 늘었다.
주목할 점은 node-ipc 사례의 수법이다. 여러 메이저 버전 라인에 동시에 악성 버전을 배포했다. 이건 우연이 아니라 피해 범위를 최대화하려는 의도적인 선택이다. ^1.0.0을 쓰는 사람도, ^2.0.0을 쓰는 사람도 다음 설치 때 자동으로 악성 버전을 받게 된다.

락파일은 필수지만 만능이 아니다
첫 번째 방어선은 락파일이다. package-lock.json이나 yarn.lock, pnpm-lock.yaml을 반드시 커밋한다.
락파일이 하는 일은 명확하다. 의존성 트리 전체의 정확한 버전과 무결성 해시를 고정한다. 락파일이 있으면 ^1.2.3 같은 범위 표기에도 불구하고 매번 같은 버전이 설치된다.
그런데 락파일에는 한계가 있다. 락파일이 갱신되는 순간 새 버전이 들어온다. 의존성을 하나 추가하거나 업데이트하면 락파일이 다시 계산되고, 그때 악성 버전이 들어올 수 있다. 락파일 자체를 조작하는 공격도 있다.
그래서 락파일과 함께 가야 하는 것이 설치 명령이다.
npm ci # 락파일 그대로 설치. 락파일과 package.json 이 어긋나면 실패
npm install # 락파일을 갱신할 수 있음
CI에서는 반드시 npm ci를 써야 한다. npm install을 쓰면 CI가 락파일을 바꿔버릴 수 있고, 그러면 로컬에서 검증한 것과 다른 것이 배포된다. yarn은 --frozen-lockfile, pnpm은 --frozen-lockfile이 같은 역할을 한다.
쿨다운: 새로 나온 버전을 바로 받지 않는다
락파일이 오염되는 것 자체를 막는 방법으로 최근 주목받는 것이 쿨다운(cooldown)이다. 배포된 지 일정 기간이 지나지 않은 버전은 설치 대상에서 제외하는 것이다.
근거는 통계적이다. 악성 버전은 대개 배포 후 수 시간에서 수일 안에 발견되고 내려진다. 새 버전을 며칠 묵혔다가 받으면, 그 사이에 커뮤니티가 걸러준 것을 받게 된다.
// .npmrc 또는 패키지 관리자 설정으로 최소 배포 경과 시간을 지정
// (지원 여부는 사용하는 패키지 관리자 버전에 따라 다르다)
{
"minimumReleaseAge": "7d"
}
새 기능을 일주일 늦게 받는 것과, 탈취된 패키지를 즉시 받는 것 중 무엇이 나은지는 명확하다. 긴급 보안 패치는 예외로 두면 된다.
업데이트를 소스 코드처럼 리뷰한다
여기가 실제로 가장 지켜지지 않는 부분이다. 의존성 업데이트 PR은 대개 봇이 올리고, 사람은 CI가 초록색인 것만 보고 병합한다.
권장되는 태도는 명확하다. 모든 의존성 변경을 소스 코드 변경처럼 다룬다. 구체적으로는 이렇게 한다.
메이저 버전 업그레이드는 변경 로그를 읽는다. 패치 버전이라도 새 관리자가 인수한 패키지는 주의한다. 오래 방치된 인기 패키지를 누군가 넘겨받은 뒤 악성 코드를 넣은 사례가 반복됐다.
postinstall 스크립트가 새로 생겼는지 본다. 공급망 공격의 상당수가 설치 스크립트를 통해 실행된다. 설치 스크립트를 아예 막는 방법도 있다.
npm ci --ignore-scripts
이러면 일부 네이티브 모듈 빌드가 안 되므로, 필요한 패키지만 예외로 허용하는 식으로 운영한다.
그래서 무엇부터 할 것인가
우선순위를 두면 이렇다.

npm audit은 알려진 취약점만 잡지만 없는 것보다 낫고, CI에 넣어두면 새 취약점이 공개됐을 때 알림이 온다.
완전한 방어는 없다는 전제
마지막으로 균형을 잡고 싶다. 위 조치를 다 해도 제로데이 공급망 공격은 막을 수 없다. 아무도 모르는 악성 버전이 락파일에 들어오는 순간은 막을 방법이 없다.
그래서 예방만큼 중요한 것이 피해 축소다. 빌드 서버에 왜 프로덕션 데이터베이스 자격 증명이 있는지 생각해 봐야 한다. CI 러너가 왜 네트워크 전체에 접근할 수 있는지도 마찬가지다. 토큰의 유효 기간이 짧고 권한이 좁으면, 탈취돼도 할 수 있는 일이 적다.
의존성을 줄이는 것도 근본적인 방어다. 세 줄짜리 유틸리티를 위해 패키지를 깔면, 그 패키지의 의존성까지 신뢰 범위에 들어온다. 직접 쓰는 편이 나은 코드가 생각보다 많다. 공격 표면은 의존성 개수에 비례한다.
참고 자료
- npm Supply Chain Security in 2026 — Mondoo — 패키지 관리자가 막아주는 것과 못 막는 것
- Keyv and Cacheable npm Packages Compromised — Cloudsmith — 자기 전파형 웜의 확산 경위와 영향 범위
- Active Supply Chain Attack: Malicious node-ipc Versions — StepSecurity — 여러 메이저 버전 동시 배포 수법
- How to Secure Your npm Supply Chain in 2026 — PkgPulse — 락파일·npm ci·쿨다운을 포함한 방어 목록
'IT' 카테고리의 다른 글
| AI 코딩 에이전트 시대의 코드 리뷰 (0) | 2026.10.11 |
|---|---|
| Git 브랜치 전략, rebase와 merge 중 무엇을 쓸까 (0) | 2026.10.10 |
| 도커 이미지 크기 줄이기, 멀티스테이지와 distroless (0) | 2026.10.08 |
| Node vs Deno vs Bun, 2026년 런타임 선택 기준 (0) | 2026.10.07 |
| PostgreSQL 19 REPACK, 새벽 점검이 줄어든다 (0) | 2026.10.06 |
