콘텐츠로 이동

npm 12 설치 스크립트 기본 차단

npm 12 설치 스크립트 기본 차단 thumbnail
npm 12는 의존성 설치 중 자동 실행되던 라이프사이클 스크립트를 기본 차단한다. GitHub changelog 기준 공개일은 2026-07-08이고, 보도 기준 공개일은 2026-07-09이다.

요약

npm 12는 의존성 설치 중 자동 실행되던 라이프사이클 스크립트를 기본 차단한다. GitHub changelog 기준 공개일은 2026-07-08이고, 보도 기준 공개일은 2026-07-09이다.

이 변경은 특정 취약점 패치가 아니라 npm의 보안 기본값 변경이다. 악성 패키지가 preinstall, install, postinstall 같은 설치 단계 스크립트로 개발자 장비나 CI 실행 환경에서 코드를 실행하는 경로를 기본값에서 줄인다.

항목 내용
분류 공급망 보안
공개일 2026-07-08, 2026-07-09
영향 지점 npm install, npm ci 중 의존성 라이프사이클 스크립트 자동 실행
대표 스크립트 preinstall, install, postinstall
함께 제한되는 동작 암시적 node-gyp 빌드, Git 의존성, 원격 URL 의존성
줄어드는 공격면 설치 시 코드 실행
영향 가능 영역 네이티브 애드온 빌드, 바이너리 다운로드, 코드 생성, Git·file·link 의존성의 prepare 단계

핵심 변화

기존 npm 설치 흐름에서는 의존성 패키지의 설치 스크립트가 자동 실행될 수 있었다. 공격자는 오타 패키지, dependency confusion, 정상 패키지 탈취, 악성 업데이트 같은 공급망 경로를 통해 설치 순간의 로컬 명령 실행을 노릴 수 있었다.

npm 12에서는 allowScripts 기본값이 꺼진다. 의존성의 preinstall, install, postinstall 스크립트와 암시적 node-gyp 빌드는 명시적으로 허용된 경우에만 실행된다. Git 의존성은 --allow-git 허용 없이는 해석되지 않고, HTTPS tarball 같은 원격 URL 의존성도 --allow-remote 허용 없이는 해석되지 않는다.

{
  "scripts": {
    "preinstall": "node ./scripts/check-env.js",
    "install": "node-gyp rebuild",
    "postinstall": "node ./scripts/download-binary.js",
    "prepare": "node ./scripts/build.js"
  }
}

위 스크립트가 곧 악성이라는 뜻은 아니다. 문제는 이 실행 지점이 의존성 설치 흐름에 묶여 있고, 개발자 권한, CI 권한, registry token, 환경 변수에 접근 가능한 프로세스로 동작할 수 있다는 점이다.

전환 기준

필요한 패키지만 npm approve-scripts --allow-scripts-pending로 검토해 허용하고, 생성된 allowlist를 package.json에 남긴다. 프로젝트 전체에 설치 스크립트 예외를 주면 npm 12 기본 차단의 효과가 크게 줄어든다.

공격 흐름에서 줄어드는 지점

단계 기존 위험 npm 12 기본 차단 후 의미
패키지 유입 오타 패키지, 탈취된 정상 패키지, dependency confusion 유입 자체는 여전히 가능
설치 의존성 라이프사이클 스크립트 자동 실행 기본값에서 자동 실행 경로 축소
실행 지점 shell 명령, Node.js child process, 파일 쓰기, 외부 요청 명시 허용 없이는 설치 단계 실행 난도 증가
후속 영향 token 탈취, source exfiltration, CI secret 접근 실행 성공 조건이 더 좁아짐

패키지 내용의 신뢰 문제가 사라지는 것은 아니다. 런타임 import 시 실행되는 코드, 빌드 단계에서 직접 호출되는 스크립트, 애플리케이션 코드에 섞인 payload는 그대로 검토 대상이다. npm 12 변경은 설치 시 자동 실행을 기본값에서 제한하는 조치다.

자산 식별

저장소에서 라이프사이클 스크립트를 가진 내부 패키지와 직접 의존성을 먼저 찾는다. lockfile만 보면 패키지 매니저 버전과 lockfile 형식에 따라 누락될 수 있으므로 package.json 단위 점검을 같이 둔다.

find . -name package.json -not -path "*/node_modules/*" -print0 |
  xargs -0 jq -r '
    select(.scripts != null) |
    . as $pkg |
    .scripts |
    to_entries[] |
    select(.key | test("^(preinstall|install|postinstall|prepare)$")) |
    "\($pkg.name // input_filename): \(.key)=\(.value)"
  '

CI에서는 Node.js, npm 버전과 설치 정책을 같이 남긴다. 같은 lockfile이라도 npm 11과 npm 12의 기본 동작이 달라질 수 있다.

node --version
npm --version
npm config list

전환 시 확인할 지점

npm 12 도입 전에는 설치 단계에서 깨지는 빌드를 먼저 찾는다. 영향이 잦은 지점은 네이티브 애드온 컴파일, 브라우저 바이너리 다운로드, protobuf 같은 코드 생성, Git·file·link 의존성의 prepare 스크립트다.

허용이 필요한 패키지는 빌드에 필요한 항목만 남긴다. 해당 패키지가 어떤 파일을 만들고, 어떤 네트워크 요청을 하며, 어떤 환경 변수에 접근하는지 확인한 뒤 예외를 둔다.

registry token도 같이 정리한다. 설치 단계에서 token이 존재하는 CI 실행 환경은 악성 라이프사이클 스크립트의 주요 목표가 된다. publish 권한과 install 권한을 분리하고, 장기 token 대신 제한된 수명의 token이나 OIDC 기반 trusted publishing을 우선 검토한다.

탐지 포인트

명시적으로 허용된 패키지나 npm 11 이하 환경에서는 설치 스크립트 기반 흔적이 계속 남을 수 있다. npm ci 직후의 child process 생성과 외부 통신을 우선 본다.

네트워크

network_signals:
  - unexpected outbound request from CI execution environment
  - package install step contacting non-registry host
  - binary download from newly registered or low-reputation domain

프로세스

process_signals:
  - parent_process: npm
    child_process:
      - node
      - sh
      - bash
      - powershell
      - cmd
      - curl
      - wget
      - python
    context:
      - npm install
      - npm ci
      - preinstall
      - install
      - postinstall
      - prepare

파일

file_signals:
  - writes under temporary directories during install
  - modification of shell profiles or npm config
  - creation of executable files outside project build directories
  - access to .npmrc, .env, cloud credential files, or CI secret-mounted paths

publish token, cloud credential, .npmrc, .env가 존재하는 CI 실행 환경에서 설치 단계 네트워크 통신이 발생하면 확인 대상이다. 정상 패키지의 바이너리 다운로드일 수도 있으므로 도메인, 파일 해시, 생성 파일 경로를 고정해 관측한다.

참고 자료