npm Supply Chain Security: Mistakes I Made Publishing My First Packages
npm 패키지 퍼블리싱 초보자가 pnpm 도입, 프로비넌스 OIDC 설정, 파일 화이트리스트 지정으로 공급망 보안 취약점 4가지 제거
AI 요약
Context
Node.js 패키지 퍼블리싱 시 postinstall 스크립트 자동 실행(ua-parser-js 2021, colors/faker 2022, @ledgerhq/connect-kit 2023)으로 인한 공급망 공격, 프로비넌스 미설정 시 자동 실패, 자동화 토큰의 2FA 우회, 잘못된 패키지 매니저 사용으로 인한 lockfile 오염 등 npm 보안 4가지 문제가 발생했다.
Technical Solution
- postinstall 스크립트 차단: pnpm으로 마이그레이션하여 의존성의 postinstall 자동 실행 방지(npm query ':has(> .scripts[postinstall])' 명령으로 검사)
- 프로비넌스 OIDC 자동화: GitHub Actions 워크플로우에서 id-token: write 권한 추가 후 npm publish --provenance 실행하여 특정 커밋과 Actions 실행에 tarball 링크
- 자동화 토큰 스코핑: npm token create --cidr= --publish --package=패키지명으로 CI 환경 IP 범위와 특정 패키지로 제한된 토큰 생성
- Lockfile 강제화: .npmrc에 frozen-lockfile=true, engine-strict=true 설정 및 package.json의 engines 필드에서 pnpm>=9 명시하여 npm install 실행 차단
- 배포 파일 명시: package.json에 "files": ["dist", "README.md"] 필드 추가하여 .gitignore가 아닌 파일 명시적 제한, npm pack --dry-run으로 매번 검증
- postinstall 선택적 실행: pnpm install --ignore-scripts로 의존성 다운로드만 수행 후 필요한 패키지만 pnpm rebuild esbuild로 재구성
Impact
GitHub Actions 워크플로우 1줄(id-token: write) 누락으로 30분 동안 프로비넌스 미적용 상태 지속, npm audit 알림 4건 중 실제 도달 가능한 경로 없음(모두 transitive dev 의존성), 첫 배포 전 hour 1시간 의존성 검사 필요.
Key Takeaway
npm 생태계의 보안 경계는 "패키지 설치"와 "임의 코드 실행" 사이에 없으므로, 11개 직접 의존성 수준에서도 pnpm + 프로비넌스 + 토큰 스코핑 + 파일 화이트리스트 4가지 계층 방어를 동시에 적용해야 공급망 공격 노출을 최소화할 수 있다.
실천 포인트
npm 패키지를 처음 퍼블리싱하는 팀에서는 GitHub Actions 워크플로우의 permissions.id-token: write 추가와 npm publish --provenance 옵션으로 postinstall 자동 실행 공격 방지, pnpm 도입으로 의존성의 임의 스크립트 실행 차단, package.json의 files 필드로 민감한 파일 누출 방지, 자동화 토큰의 CIDR/패키지 범위 제한으로 장시간 유효한 토큰의 피해 최소화를 동시 적용하면 공급망 공격으로부터 사용자 환경 노출을 근본적으로 줄일 수 있다.