"전형적인 Node 앱에서 프로덕션에 도는 코드 99% 가 만난 적 없는 사람들이 짠 거. 매 `npm install` 이 신뢰 결정이야."
Attack Surface
네 코드는 5,000 줄쯤. 전형적인 npm install 후 node_modules 가 200MB 고 300 다른 저자의 800 패키지 포함. 그 패키지들 각자 가능:
- install 시간에 임의 코드 실행 (npm lifecycle script 통해).
- import 시간에 임의 코드 실행 (top-level side effect 통해).
- 네트워크 요청, 파일 읽기, 프로세스 spawn — Node 가 할 수 있는 모든 거.
대부분 이 중 아무것도 안 함. 근데 몇몇이 역사적으로 했어. npm 레지스트리가 typosquatting, 계정 takeover, 인기 패키지의 악성 업데이트 본 적 있어. Supply chain 이 진짜야.
실제로 중요한 다섯 습관
package-lock.json / pnpm-lock.yaml 이 모든 패키지를 특정 버전 + checksum 으로 pin. 없으면 install 마다 다른 의존성 트리 — 다른 attack surface.2. CI 에선
npm ci (또는 pnpm install --frozen-lockfile) 써. package.json 과 lockfile 안 맞으면 설치 거부. Silent drift 멈춤.3.
npm audit 정기 실행. 의존성의 알려진 CVE 나열. 완벽 아냐 — 비악용 가능 이슈의 false positive, 미보고 버그의 false negative — 근데 floor 야.4. 안 필요한 lifecycle script 비활성화.
npm install --ignore-scripts 또는 pnpm 의 onlyBuiltDependencies allowlist (pnpm 9+) 가 임의의 postinstall 코드 실행 막음. 어느 dep 가 자기 script 필요한지 감사; 그것만 allowlist.5. 런타임 dep 만이 아닌 tooling 도 pin. 침해된 devDependency 가 출하 전 빌드 출력에 악성 코드 쓸 수 있음. TypeScript, ESLint, Vite, 다 pin — 런타임 dep 만큼 보안-크리티컬.
방어 깊이로의 Permissions 모델
Track 6 의 permissions 모델이 네 마지막 줄: 악성 dep 가 네 프로세스에서 실행돼도 node --permission --allow-fs-read=./data ... 가 뭐 읽고 쓰고 call out 할지 제한. ~/.ssh/ 읽으려는 침해된 dep 가 ERR_ACCESS_DENIED 치고 시끄럽게 실패. Dep 신뢰해도 쓸 가치 있어.
Lockfile 이 마법 아냐
v1.2.3 에 pin 은 "이 정확한 tarball 의 버전 1.2.3" 의미. 1.2.3 이 악성 릴리스였으면 lockfile 이 너를 악성 코드에 pin. Lockfile 은 silent 업데이트 보호, 침해된 릴리스 보호 아님. 그거 방어는 옛-천천히 설치: dep 릴리스 날 업그레이드 안 함, 1-2 주 기다림, 커뮤니티가 vet 하게 둠. 수동 리뷰 있는 npm-check-updates 가 npm update --latest 이김.
npm audit 이 놓치는 거
npm audit 이 GitHub Advisory Database 검사. 놓침:
- 미보고 취약점 (대부분).
- 활성 advisory 없는 패키지의 취약점.
- 로직-레벨 이슈 (dep 가 합법적-인데-원치-않는 거 함).
- 아직 flag 안 된 fresh 릴리스의 악성 코드.
npm audit 을 floor 로 다뤄. 더 높은 신뢰엔 socket.dev 나 snyk 같은 도구가 패키지를 더 폭넓게 분석 (네트워크 동작, lifecycle script, 의존성 drift). 민감 데이터 처리하는 프로덕션 서비스엔 비용 가치 있어.