npm / pnpm / npx / pnpm dlx サプライチェーン攻撃対策設定ガイド(完全版)
npx と pnpm dlx はロックファイルを介さず に任意のパッケージをリモートから即時実行できるツールであり、通常の npm install / pnpm install よりも本質的に高いリスクを持つ。特に npx は CI 環境(stdin が TTY でないとき)に --yes を自動的に適用するため、悪意あるタイポスクワットパッケージを確認なしにインストール・実行する 可能性がある1 。pnpm v10 は minimumReleaseAge・trustPolicy・blockExoticSubdeps という npm にはないセキュリティ設定を備えており2 、pnpm dlx もこれらの設定の恩恵を受ける。本レポートでは、各ツール固有のリスクと、.npmrc・pnpm-workspace.yaml・コマンドフラグを通じた対策を網羅する。
┌─────────────────────────────────────────────────────────────────────┐
│ サプライチェーン攻撃の経路 │
│ │
│ ① タイポスクワット │
│ npx crete-react-app → 悪意あるパッケージを実行 │
│ │
│ ② Dependency Confusion │
│ プライベートパッケージ名と同名のパブリックパッケージを公開 │
│ │
│ ③ アカウント乗っ取り → 正規パッケージへの悪意ある版の公開 │
│ │
│ ④ postinstall スクリプト悪用 │
│ install/postinstall フックで任意コードを実行 │
│ │
│ ⑤ Manifest Confusion │
│ tarball と manifest の内容の不一致を悪用 │
│ (npm レジストリの既知の構造的問題) │
│ │
│ ⑥ npx/pnpm dlx 固有: 非バージョン固定実行 │
│ npx package@latest → 最新版(悪意あるかもしれない)を実行 │
└─────────────────────────────────────────────────────────────────────┘
2. npx / npm exec 固有のリスクと設定
npx は npm v7 で npm exec の薄いラッパーとして再実装された1 。以下の挙動がセキュリティ上の問題となる:
# stdin が TTY でない場合、または CI 環境変数が設定されている場合
# npm exec は --yes を自動的に適用する
$ CI=true npx unknown-package # 確認なしで即インストール・実行
CI 環境変数(CI, CONTINUOUS_INTEGRATION, BUILD_NUMBER, RUN_ID)が設定されている場合、npm exec は自動的に --yes 相当の挙動をする1 。これはパイプラインで悪意あるパッケージが無確認でインストールされる原因となる。
npx / npm exec は package-lock.json を参照しない。npm install と異なり、実行のたびにレジストリから最新の解決を行う。
③ バージョン未指定では latest タグを使用
# 危険: latest タグが指す版は攻撃者によって変更可能
npx create-react-app my-app
# 安全: 特定バージョンを固定
npx create-react-app@5.0.1 my-app
# 環境変数でデフォルト yes を無効化
export npm_config_yes=false
# または .npmrc に設定
# yes=false ← これはインストールのデフォルト挙動のみに影響
注意 : npm exec の CI auto-yes は環境変数ベースの検出で行われるため、
npm_config_yes=false を CI スクリプト先頭で設定することで上書きできる。
設定2: --no フラグでリモートインストールを拒否
# ローカルに存在しない場合はエラーで終了(インストールしない)
npx --no some-tool
# または npm exec 形式で
npm exec --no -- some-tool
--no を指定すると、ローカルプロジェクトの node_modules または npm キャッシュにパッケージが存在しない場合にエラーで終了する3 。
# ❌ 危険: latest が悪意ある版に置き換わる可能性
npx create-next-app@latest
# ✅ 安全: ハッシュまたは完全バージョンで固定
npx create-next-app@15.3.0
# ✅ さらに安全: --package で明示指定
npm exec --package=create-next-app@15.3.0 -- create-next-app my-app
# 信頼できるレジストリのみを使用
npx --registry=https://registry.npmjs.org/ create-react-app@5.0.1 my-app
.npmrc でデフォルトレジストリを設定している場合も、--registry フラグで上書き可能。
設定5: ignore-scripts との組み合わせ
npm exec は npm install と同様に .npmrc の ignore-scripts を尊重する:
# ~/.npmrc
ignore-scripts =true
ただし ignore-scripts=true のグローバル設定は、npx で実行したパッケージのスクリプト(npx 経由のパッケージ自体の実行)には影響しない 。あくまでインストール時の preinstall/postinstall を防ぐ設定であることに注意。
設定6: --offline を使ってキャッシュのみから実行
# ネットワークアクセスなしでキャッシュのみ使用(予め準備が必要)
npx --offline create-react-app@5.0.1 my-app
--offline 設定はキャッシュに存在しないパッケージの場合にエラーで終了する3 。
設定7: .npmrc でスコープ別レジストリを強制
# ~/.npmrc または プロジェクト .npmrc
# @myorg スコープのみプライベートレジストリから取得
@myorg:registry =https://private.registry.example.com/
これにより npx @myorg/tool は常にプライベートレジストリのみを参照する。
危険なパターン
安全な代替
理由
npx some-tool
npx some-tool@1.2.3
バージョン未固定はタグ変更に脆弱
npx -y some-tool
npx --no some-tool
-y は確認なしでインストール実行
CI=true npx some-tool
バージョン固定 + npm_config_yes=false
CI では auto-yes が有効
シェルコマンドのコピペ実行
内容を確認してから
タイポスクワット攻撃の標的
npx npm@6 install --prefer-offline
npm ci
Manifest Confusion に脆弱4
# ~/.npmrc または <project>/.npmrc
# postinstall スクリプトを無効化(最重要)
ignore-scripts =true
# 脆弱性監査
audit =true
audit-level =high
# バージョン完全固定(^ なし)
save-exact =true
# SSL 検証強制(デフォルト true だが明示)
strict-ssl =true
# プライベートスコープ向けレジストリ設定(Dependency Confusion 対策)
@myorg:registry =https://private.registry.example.com/
//private.registry.example.com/:_authToken =${NPM_PRIVATE_TOKEN}
# npm v9.5.0 以降: SLSA プロベナンスと署名を検証
npm audit signatures
# ロックファイル必須・内容一致チェック
npm ci
# 脆弱性チェック(high 以上でブロック)
npm audit --audit-level=high
# 署名検証
npm audit signatures
3-4. パッケージ publish 時のプロベナンス
# GitHub Actions での publish 時
- run : npm publish --provenance --access public
env :
NODE_AUTH_TOKEN : ${{ secrets.NPM_TOKEN }}
または .npmrc に:
4-1. pnpm-workspace.yaml セキュリティ設定
# pnpm-workspace.yaml - セキュリティ重視設定
# ★ 新規公開パッケージの遅延インストール(悪意あるバージョンの発見を待つ)
# 悪意ある公開バージョンはほとんどの場合1時間以内に検出・削除される
minimumReleaseAge : 1440 # 24時間
# ★ 信頼レベルの降格を禁止
# アカウント乗っ取り後に署名なしで公開された版のインストールを阻止
trustPolicy : no-downgrade
# ★ 間接依存の非レジストリソースを禁止
# git URL / tarball URL 経由での悪意あるコード混入を防止
blockExoticSubdeps : true
# 脆弱性設定
auditConfig :
ignoreCves : []
ignoreGhsas : []
# 脆弱な間接依存の強制更新
overrides : {}
# ignore-scripts=true # 必要に応じて(pnpm dlx に影響あり - 後述)
strict-ssl =true
save-exact =true
@myorg:registry =https://private.registry.example.com/
# CI 環境では自動的に --frozen-lockfile 相当
pnpm install --frozen-lockfile
pnpm audit --audit-level=high
5. pnpm dlx 固有のリスクと設定(最重要)
pnpm dlx(pnpx とも)はレジストリからパッケージを一時的にフェッチし、実行後に破棄するコマンド5 。npx に対応する pnpm のコマンドだが、いくつかの重要な違いがある。
① postinstall スクリプトはデフォルトで実行される
pnpm dlx で実行する直接対象のパッケージ は、ignore-scripts の設定に関わらず postinstall スクリプトが許可される:
"The actual packages executed by dlx are allowed to run postinstall scripts by default."5
つまり .npmrc に ignore-scripts=true を設定していても、pnpm dlx my-package を実行すると my-package の postinstall が実行される。
# 危険: 依存パッケージのビルドスクリプトを許可
pnpm --allow-build=esbuild my-bundler bundle
# --allow-build に指定しないパッケージの postinstall は禁止される
# しかし dlx 本体は常に許可される点に注意
# 危険: 最新版(悪意ある版かもしれない)を実行
pnpm dlx create-vite
# 安全: バージョン固定
pnpm dlx create-vite@6.3.5
# ❌ 危険
pnpm dlx create-vue
pnpm dlx create-next-app@latest
# ✅ 安全: 完全バージョン固定
pnpm dlx create-vue@3.17.0
pnpm dlx create-next-app@15.3.0
設定2: --allow-build で許可パッケージを最小限に
# デフォルトは直接実行パッケージのみ postinstall を許可
# 依存関係に postinstall が必要な場合のみ明示的に許可
pnpm --allow-build=esbuild dlx my-bundler@1.0.0 build
--allow-build を指定しない場合、pnpm dlx が実行するパッケージ自体の postinstall のみ実行される。依存関係の postinstall は実行されない。
設定3: minimumReleaseAge で新規公開版の実行を遅延
pnpm-workspace.yaml の minimumReleaseAge 設定は pnpm dlx にも適用される:
# pnpm-workspace.yaml
minimumReleaseAge : 60 # dlx でも60分未満の新規公開版をブロック
設定4: trustPolicy で信頼レベル降格をブロック
# pnpm-workspace.yaml
trustPolicy : no-downgrade
# dlx で実行するパッケージも信頼レベルが下がっていれば失敗
設定5: --package で実行パッケージを明示
# 暗黙的なパッケージ名推測を回避
pnpm --package=create-vue@3.17.0 dlx create-vue my-app
# 複数パッケージが必要な場合
pnpm --package=yo@5.0.0 --package=generator-webapp@8.0.0 dlx yo webapp
5-4. pnpm dlx と npx の挙動差異
特性
npx (npm exec)
pnpm dlx
CI での auto-yes
✅ 自動適用(危険)
❌ 明示的な確認なし(直接実行)
ignore-scripts の適用
インストール時に適用
直接実行パッケージは常に許可
バージョン未指定時の解決
latest タグを使用
latest タグを使用
postinstall のデフォルト
ignore-scripts 設定に従う
直接実行パッケージは常に実行
ロックファイルの参照
参照しない
参照しない
minimumReleaseAge の適用
❌ npm にはない
✅ 適用される(v10.16.0+)
trustPolicy の適用
❌ npm にはない
✅ 適用される(v10.21.0+)
6. Manifest Confusion 脆弱性への対応
npm レジストリには、パッケージの manifest(レジストリに保存されるメタデータ)と tarball 内の package.json が一致することが保証されていない構造的問題がある4 。
攻撃者が scripts: {} と dependencies: {} が空の manifest を持ちながら、tarball 内の package.json には悪意ある postinstall スクリプトを含めて公開する
セキュリティスキャナー(Snyk, npm audit等)は manifest を参照するためスクリプトを検知しない
npm install は tarball 内の package.json を実行するため悪意あるスクリプトが実行される
# postinstall スクリプトを完全無効化(最も確実な対策)
ignore-scripts =true
この脆弱性(2023年3月公開)は現時点でも npm レジストリサーバー側での完全な修正はされていないため、ignore-scripts=true が最も効果的な対策4 。
設定
適用先
設定場所
攻撃対策
ignore-scripts=true
npm install / pnpm install
.npmrc
postinstall 悪用
audit=true + audit-level=high
npm/pnpm install
.npmrc / workspace.yaml
既知脆弱性
save-exact=true
npm install
.npmrc
意図しないバージョン更新
strict-ssl=true
全通信
.npmrc
MITM
@scope:registry=...
スコープパッケージ
.npmrc
Dependency Confusion
npm ci / pnpm install --frozen-lockfile
CI
CLI
ロックファイル整合性
npm audit signatures
インストール後
CLI
パッケージ署名改ざん
minimumReleaseAge: 1440
pnpm install / dlx
pnpm-workspace.yaml
新規悪意ある公開版
trustPolicy: no-downgrade
pnpm install / dlx
pnpm-workspace.yaml
アカウント乗っ取り後の版
blockExoticSubdeps: true
pnpm install / dlx
pnpm-workspace.yaml
git URL 経由の混入
npx pkg@X.Y.Z
npx 実行
CLI
タイポスクワット
pnpm dlx pkg@X.Y.Z
pnpm dlx 実行
CLI
タイポスクワット
npx --no pkg
npx 実行
CLI
意図しないインストール
npm_config_yes=false
CI 環境
環境変数
CI auto-yes 無効化
--allow-build=<pkg>
pnpm dlx
CLI
postinstall 最小化
npm profile enable-2fa auth-and-writes
npm アカウント
CLI
アカウント乗っ取り
provenance=true
npm publish
.npmrc
サプライチェーン透明性
# ~/.npmrc(グローバル設定)
ignore-scripts =true
audit =true
audit-level =high
save-exact =true
strict-ssl =true
# スコープ別プライベートレジストリ(Dependency Confusion 対策)
@myorg:registry =https://your-private-registry.example.com/
//your-private-registry.example.com/:_authToken =${NPM_PRIVATE_TOKEN}
# CI パイプライン (GitHub Actions 等)
export npm_config_yes=false # npx の CI auto-yes を無効化
npm ci
npm audit --audit-level=high
npm audit signatures # npm v9.5.0+
# npx を使う場合の必須ルール
npx package@X.Y.Z # 常にバージョン固定
npx --no package@X.Y.Z # ローカルにない場合はエラー
# pnpm-workspace.yaml
minimumReleaseAge : 1440 # 24時間未満の公開版をブロック
trustPolicy : no-downgrade # 信頼レベルの降格を禁止
blockExoticSubdeps : true # 間接依存の git/tarball ソースを禁止
auditConfig :
ignoreCves : []
# .npmrc(pnpm プロジェクト)
strict-ssl =true
save-exact =true
@myorg:registry =https://your-private-registry.example.com/
# CI パイプライン
pnpm install --frozen-lockfile
pnpm audit --audit-level=high
# pnpm dlx を使う場合の必須ルール
pnpm dlx package@X.Y.Z # 常にバージョン固定
pnpm --allow-build=pkg1 dlx tool@X.Y.Z # postinstall は最小限に
インシデント
攻撃手法
有効な対策設定
eslint-scope (2018)
アカウント乗っ取り → 悪意あるバージョン公開
trustPolicy: no-downgrade, 2FA auth-and-writes
event-stream (2018)
既存パッケージへの悪意ある依存追加
ignore-scripts, minimumReleaseAge
UAParser.js (2021)
アカウント乗っ取り → 悪意あるバージョン公開
trustPolicy: no-downgrade, minimumReleaseAge
crossenv (2017)
タイポスクワット
スコープパッケージ化、npx pkg@version の徹底
node-ipc (2022)
メンテナー自身による悪意あるコード追加
ignore-scripts, minimumReleaseAge
項目
信頼度
根拠
npx の CI 環境での auto-yes 挙動
高
npm 公式 npm exec ドキュメント1 より直接確認
pnpm dlx の postinstall 挙動
高
pnpm 公式 pnpm dlx ドキュメント5 より直接確認
minimumReleaseAge, trustPolicy, blockExoticSubdeps
高
pnpm 公式 Settings ドキュメント2 より直接確認
Manifest Confusion 脆弱性
高
npm CLI 元チームマネージャーによる技術的調査記事4 より
npm_config_yes=false による CI auto-yes の無効化
中
npm config 仕様から導出(直接ドキュメント記載なし)
ignore-scripts が dlx 本体に適用されない件
高
pnpm dlx ドキュメントに明示的に記載5
Footnotes