Skip to content

Instantly share code, notes, and snippets.

@podhmo
Last active April 9, 2026 10:00
Show Gist options
  • Select an option

  • Save podhmo/d72566411eddd2d13a2e11b9b06e9d7c to your computer and use it in GitHub Desktop.

Select an option

Save podhmo/d72566411eddd2d13a2e11b9b06e9d7c to your computer and use it in GitHub Desktop.
github copilot cliの/researchコマンドの使用感調査をしてみたところ、古い情報を掴まされることがあるかもしれない。

github copilot cliの/researchコマンドの使用感調査

claude codeは知らないが、github copilot cliには /research コマンドが存在する。こんな感じで試せる。

  • /research user settings for prevent suply-chain attack in npm,pnpm
  • /research user configurations to prevent supply chain attacks in npm, pnpm, and especially pnpm dlx and npx.
  • /research npm,npx環境でのpnpmのminimumReleaseAgeの代わりになるもの,uv,uvx環境での同様のもの。それのユーザーレベルの設定方法について調べて欲しい

古い情報を見てしまうかも?

結構便利なのだけどnpm v11の min-release-age を見つけてこなかった。

というわけで自分で調べてわかっているページを渡して更新してもらった(その時も何回もcurlで色々やったりpythonを呼んだり色々してた) 本当は https://docs.npmjs.com/cli/v11/using-npm/config#min-release-age を見つけて欲しかった。

(これはモデルがnpmの最新をv10だと思ってv10の記事を探しに行ってしまうか、手元の環境がnpm v10@10.9.2なのでnpmのv10を探してしまうようだ。。)

手元の環境で早合点されるとすると、古い環境で最新のものを探すという行為が機能しなくなるのか。。 あと、ページ長すぎると途中で諦める。まぁそれはそうか。。

ちなみに振り返ってみてなぜあなた独自ではmin-release-ageの存在を確認できなかったのでしょうか?

振り返ると、主に3つの失敗がありました。


  1. ドキュメントのバージョンを古い方で参照した

前の調査で参照していたのは npm v10 のドキュメント:

https://docs.npmjs.com/cli/v10/using-npm/config#before

min-release-age は v11 で追加された機能のため、v10ドキュメントには存在しません。ローカルの npm が v10.9.2 だったことで「v10が現行」と暗黙的に判断してしまいました。


  1. web_fetch のアンカーリンクが機能しない

今回ユーザーが教えてくれたURL:

https://docs.npmjs.com/cli/v11/using-npm/config#min-release-age

#min-release-age というフラグメント識別子を指定して web_fetch しましたが、このツールはページ先頭から返すため、アンカーは無視されます。min-release-age のセクションはページの約30,000文字目にあり、最初のフェッチ(5,000文字)では到達できませんでした。


  1. ページが長すぎて途中で諦めた

start_index を増やして読み進めるべきでしたが、途中(13,000文字付近)でTOCに min-release-age がリストされているのを見落としたまま別のアプローチ(GitHubのソース検索など)に切り替えてしまいました。


改善すべき点

  • 最新バージョンのドキュメントを明示的に確認する(npm --version でローカル版を確認したなら、最新版との差分を疑うべきだった)
  • 長いドキュメントは start_index を増やして複数回フェッチする
  • TOCで目的のセクション名が確認できたなら、そのオフセットまでページネーションを進める

npm/npx・uv/uvx における minimumReleaseAge 相当機能の調査

概要

pnpm の minimumReleaseAge は「公開からX分未満のパッケージをインストール拒否する」機能。
これにより、攻撃者がパッケージを乗っ取ってすぐに悪意あるバージョンを公開する「タイムオブパブリッシュ」攻撃を防ぐことができる。

本レポートでは以下を調査する:

  1. npm/npx 環境での代替手段
  2. uv/uvx 環境での代替手段(exclude-newer

1. uv/uvx 環境での代替機能(最良の代替)

exclude-newer — pnpm minimumReleaseAge の事実上の等価機能

uvには exclude-newer という設定があり、**duration形式(持続時間)**を受け入れる。これは minimumReleaseAge に非常に近い動作をする。

機能 pnpm uv
設定名 minimumReleaseAge exclude-newer
値の形式 分(整数) RFC3339日時, ISO8601 duration, "friendly"文字列
動作 X分未満のパッケージをブロック 指定時間より新しいパッケージをブロック
等価設定例 minimumReleaseAge: 1440 exclude-newer = "24 hours"

ドキュメントの説明

Limit candidate packages to those that were uploaded prior to the given date.

Accepts RFC 3339 timestamps (e.g., 2006-12-02T02:07:43Z), a "friendly" duration (e.g., 24 hours, 1 week, 30 days), or an ISO 8601 duration (e.g., PT24H, P7D, P30D).

ユーザーレベルの設定方法

設定ファイル

# ~/.config/uv/uv.toml  (macOS/Linux)
# %APPDATA%\uv\uv.toml  (Windows)

# 24時間以内に公開されたパッケージをブロック
exclude-newer = "24 hours"

# または ISO 8601 duration 形式
# exclude-newer = "PT24H"

# または RFC 3339 形式(固定日時)
# exclude-newer = "2024-01-01T00:00:00Z"

環境変数

# ~/.bashrc / ~/.zshrc に追加
export UV_EXCLUDE_NEWER="24 hours"

コマンドラインフラグ

uvx --exclude-newer "24 hours" some-tool
uv add --exclude-newer "24 hours" some-package

exclude-newer-package — パッケージ個別設定

特定パッケージのみに制限、または特定パッケージをグローバル制限から除外:

# ~/.config/uv/uv.toml
[exclude-newer-package]
# tqdm はグローバルの exclude-newer 設定から除外(信頼済みパッケージ)
tqdm = false
# 特定パッケージには個別の日時制限を設定
some-risky-package = "2022-04-04T00:00:00Z"

uvxでの注意事項

uvxuv tool run の別名であり、ツール実行時も exclude-newer が適用される。
ただし uv tool コマンドではプロジェクトレベルの設定は無視され、ユーザーレベル設定(~/.config/uv/uv.toml)とシステムレベル設定(/etc/uv/uv.toml)のみが読まれる。

# uvx でも exclude-newer は有効(ユーザー設定から適用)
uvx ruff  # ~/.config/uv/uv.toml の exclude-newer が適用される

uvのその他セキュリティ設定

no-build — ソースビルドの無効化

# uv.toml
no-build = true  # ソースディストリビューションのビルドを無効化
                 # = 任意のPythonコードが実行されない(setup.py等)

index-strategy — 依存性混乱攻撃対策

# uv.toml
# デフォルト値。複数インデックス使用時、最初に見つかったインデックスのみを使用
index-strategy = "first-index"

# 危険な設定(使用しないこと)
# index-strategy = "unsafe-best-match"

prerelease — プレリリース版の制御

# uv.toml
prerelease = "disallow"  # プレリリース版を全て拒否

2. npm/npx 環境での代替機能

min-release-age — npm v11 でネイティブサポート(✅ 発見)

npm v11 に min-release-age という設定が追加されており、pnpmの minimumReleaseAge とほぼ同等の機能を持つ。

ドキュメントの説明(npm v11)

If set, npm will build the npm tree such that only versions that were available more than the given number of days ago will be installed. If there are no versions available for the current set of dependencies, the command will error.

This flag is a complement to before, which accepts an exact date instead of a relative number of days.

This config cannot be used with: before

pnpmとの比較

pnpm minimumReleaseAge npm min-release-age
単位 (minutes) (days)
最小設定値 1分 1日
デフォルト null(無効) null(無効)
等価設定例 minimumReleaseAge: 1440 min-release-age=1

注意: pnpmは分単位で細かく設定できるが、npmは日単位のみ。
また npm v10以前には存在しない(ローカルのnpm v10で確認済み)。

ユーザーレベルの設定方法

# ~/.npmrc
# 1日(24時間)以内に公開されたバージョンをインストール拒否
min-release-age=1

# 7日以内に公開されたバージョンをインストール拒否
# min-release-age=7

コマンドラインフラグ

npm install --min-release-age=1
npm ci --min-release-age=1

環境変数

# ~/.bashrc / ~/.zshrc に追加
export npm_config_min_release_age=1

npx での利用

# npm exec (npx) でも --min-release-age フラグが利用可能(npm v11+)
npx --min-release-age=1 some-package

注意: npm v11 が必要。npm --version で確認の上、必要であれば npm install -g npm@latest でアップグレードすること。


--before フラグ — 固定日時によるスナップショット(補完的機能)

min-release-age相互排他で使用不可。固定日時でのスナップショット再現が目的。

# ~/.npmrc(固定値のみ。動的な「1日前」はシェルスクリプトが必要)
before=2024-01-01T00:00:00.000Z
# シェルスクリプトで動的計算
npm install --before=$(date -v-1d -u +%Y-%m-%dT%H:%M:%SZ)  # macOS
npm install --before=$(date -d '1 day ago' -u +%Y-%m-%dT%H:%M:%SZ)  # Linux

サードパーティツールによる代替

Socket.dev — リアルタイム検出

# socket コマンドをインストール
npm install -g @socketsecurity/cli

# socket をラッパーとして使用
socket npm install some-package  # ソケットがセキュリティチェックを実行

# CI/CD での使用
socket npm ci

Socket.dev はパッケージの「初回公開からの経過時間」「急激なダウンロード増加」などを検出する。

Snyk — 脆弱性スキャン

# npm install 後に監査
snyk test

# コマンドラインでのブロック
snyk test --severity-threshold=high

npmの設定ファイル位置

# ユーザーレベルの設定
~/.npmrc

# 設定確認
npm config list
npm config get userconfig

3. 機能比較対照表

機能 pnpm npm/npx uv/uvx
最近公開パッケージのブロック minimumReleaseAge(分単位) min-release-age(日単位, v11+ exclude-newer (duration形式)
設定の動的更新 ✅(分単位) ✅(日単位, .npmrcmin-release-age=1 ✅("24 hours"等)
ユーザーレベル設定ファイル ~/.npmrc (pnpm共通) ~/.npmrc ~/.config/uv/uv.toml
環境変数サポート npm_config_* npm_config_* UV_EXCLUDE_NEWER
スクリプト実行制御 ignore-scripts=true ignore-scripts=true no-build=true
依存性混乱対策 npmRegistryServer + スコープ @scope:registry index-strategy=first-index (デフォルト)
プロバナンス検証 - npm audit signatures -

4. 推奨設定まとめ

uv/uvx ユーザー向け推奨設定

# ~/.config/uv/uv.toml

# 24時間以内に公開されたパッケージをブロック(pnpm minimumReleaseAge相当)
exclude-newer = "24 hours"

# ソースディストリビューションのビルドを無効化(任意コード実行防止)
# ※ ビルドが必要なパッケージには影響するため環境に応じて判断
# no-build = true

# 依存性混乱攻撃対策(デフォルト値を明示)
# index-strategy = "first-index"

# プレリリースを拒否
prerelease = "disallow"

npm/npx ユーザー向け推奨設定

# ~/.npmrc

# 1日以内に公開されたバージョンをインストール拒否(npm v11+ 必須)
min-release-age=1

# スクリプト実行を無効化(最重要)
ignore-scripts=true

# 正確なバージョンピン留め
save-exact=true

# 監査の自動実行
audit=true
audit-level=moderate

# SSL/TLS検証の強制
strict-ssl=true

npm v10 以前を使用している場合は、npm install -g npm@latest でアップグレード後に min-release-age が利用可能になる。


5. まとめ・評価

ツール minimumReleaseAge 相当機能の有無 評価
pnpm ✅ ネイティブサポート (minimumReleaseAge) ★★★★★
uv/uvx exclude-newer (duration形式あり) ★★★★☆
npm v11+ min-release-age(日単位) ★★★★☆
npm v10以前 ❌ ネイティブ機能なし ★★☆☆☆
npx --min-release-age フラグ(npm v11+) ★★★☆☆

uvの exclude-newer は duration形式("24 hours", "7 days" 等)をサポートしており、pnpmの minimumReleaseAge実質的に同等の機能を提供する。
npm v11 の min-release-age も同等機能を持つが、単位が「日」のみで分単位の細かい設定はできない点が pnpm/uv より劣る。


参考リンク

npm / pnpm / npx / pnpm dlx サプライチェーン攻撃対策設定ガイド(完全版)

Executive Summary

npxpnpm dlxロックファイルを介さずに任意のパッケージをリモートから即時実行できるツールであり、通常の npm install / pnpm install よりも本質的に高いリスクを持つ。特に npx は CI 環境(stdin が TTY でないとき)に --yes を自動的に適用するため、悪意あるタイポスクワットパッケージを確認なしにインストール・実行する可能性がある1。pnpm v10 は minimumReleaseAgetrustPolicyblockExoticSubdeps という npm にはないセキュリティ設定を備えており2pnpm dlx もこれらの設定の恩恵を受ける。本レポートでは、各ツール固有のリスクと、.npmrcpnpm-workspace.yaml・コマンドフラグを通じた対策を網羅する。


1. 攻撃経路の全体像

┌─────────────────────────────────────────────────────────────────────┐
│              サプライチェーン攻撃の経路                              │
│                                                                       │
│  ① タイポスクワット                                                  │
│     npx crete-react-app → 悪意あるパッケージを実行                 │
│                                                                       │
│  ② Dependency Confusion                                               │
│     プライベートパッケージ名と同名のパブリックパッケージを公開       │
│                                                                       │
│  ③ アカウント乗っ取り → 正規パッケージへの悪意ある版の公開          │
│                                                                       │
│  ④ postinstall スクリプト悪用                                       │
│     install/postinstall フックで任意コードを実行                    │
│                                                                       │
│  ⑤ Manifest Confusion                                                │
│     tarball と manifest の内容の不一致を悪用                        │
│     (npm レジストリの既知の構造的問題)                             │
│                                                                       │
│  ⑥ npx/pnpm dlx 固有: 非バージョン固定実行                         │
│     npx package@latest → 最新版(悪意あるかもしれない)を実行       │
└─────────────────────────────────────────────────────────────────────┘

2. npx / npm exec 固有のリスクと設定

2-1. npx が特に危険な理由

npx は npm v7 で npm exec の薄いラッパーとして再実装された1。以下の挙動がセキュリティ上の問題となる:

① CI 環境での自動 --yes 適用

# 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 execpackage-lock.json を参照しない。npm install と異なり、実行のたびにレジストリから最新の解決を行う。

③ バージョン未指定では latest タグを使用

# 危険: latest タグが指す版は攻撃者によって変更可能
npx create-react-app my-app

# 安全: 特定バージョンを固定
npx create-react-app@5.0.1 my-app

2-2. npx のセキュリティ設定

設定1: CI での自動 yes を無効化

# 環境変数でデフォルト 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

設定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

設定4: レジストリを明示指定

# 信頼できるレジストリのみを使用
npx --registry=https://registry.npmjs.org/ create-react-app@5.0.1 my-app

.npmrc でデフォルトレジストリを設定している場合も、--registry フラグで上書き可能。

設定5: ignore-scripts との組み合わせ

npm execnpm install と同様に .npmrcignore-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 は常にプライベートレジストリのみを参照する。

2-3. npx の危険な使い方と安全な代替

危険なパターン 安全な代替 理由
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

3. npm インストール設定(基本)

3-1. .npmrc セキュリティ設定

# ~/.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}

3-2. パッケージ署名検証

# npm v9.5.0 以降: SLSA プロベナンスと署名を検証
npm audit signatures

3-3. CI での必須設定

# ロックファイル必須・内容一致チェック
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 に:

provenance=true

4. pnpm インストール設定

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: {}

4-2. .npmrc(pnpm 共通設定)

# ignore-scripts=true  # 必要に応じて(pnpm dlx に影響あり - 後述)
strict-ssl=true
save-exact=true
@myorg:registry=https://private.registry.example.com/

4-3. CI でのロックファイル強制

# CI 環境では自動的に --frozen-lockfile 相当
pnpm install --frozen-lockfile

pnpm audit --audit-level=high

5. pnpm dlx 固有のリスクと設定(最重要)

5-1. pnpm dlx とは

pnpm dlxpnpx とも)はレジストリからパッケージを一時的にフェッチし、実行後に破棄するコマンド5npx に対応する pnpm のコマンドだが、いくつかの重要な違いがある。

5-2. pnpm dlx 固有のリスク

① postinstall スクリプトはデフォルトで実行される

pnpm dlx で実行する直接対象のパッケージは、ignore-scripts の設定に関わらず postinstall スクリプトが許可される:

"The actual packages executed by dlx are allowed to run postinstall scripts by default."5

つまり .npmrcignore-scripts=true を設定していても、pnpm dlx my-package を実行すると my-package の postinstall が実行される。

--allow-build の意図しない使用

# 危険: 依存パッケージのビルドスクリプトを許可
pnpm --allow-build=esbuild my-bundler bundle

# --allow-build に指定しないパッケージの postinstall は禁止される
# しかし dlx 本体は常に許可される点に注意

③ バージョン未固定によるタイポスクワット

# 危険: 最新版(悪意ある版かもしれない)を実行
pnpm dlx create-vite

# 安全: バージョン固定
pnpm dlx create-vite@6.3.5

5-3. pnpm dlx のセキュリティ設定

設定1: 常にバージョンを明示固定

# ❌ 危険
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.yamlminimumReleaseAge 設定は 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

攻撃の仕組み

  1. 攻撃者が scripts: {}dependencies: {} が空の manifest を持ちながら、tarball 内の package.json には悪意ある postinstall スクリプトを含めて公開する
  2. セキュリティスキャナー(Snyk, npm audit等)は manifest を参照するためスクリプトを検知しない
  3. npm install は tarball 内の package.json を実行するため悪意あるスクリプトが実行される

対応策

# postinstall スクリプトを完全無効化(最も確実な対策)
ignore-scripts=true

この脆弱性(2023年3月公開)は現時点でも npm レジストリサーバー側での完全な修正はされていないため、ignore-scripts=true が最も効果的な対策4


7. 設定対照表(全ツール)

設定 適用先 設定場所 攻撃対策
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 サプライチェーン透明性

8. 推奨設定テンプレート

npm プロジェクト向け

# ~/.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 プロジェクト向け

# 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 は最小限に

9. 実際のインシデントと対応の関連性

インシデント 攻撃手法 有効な対策設定
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

Confidence Assessment

項目 信頼度 根拠
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

Footnotes

  1. npm 公式 npm exec / npx ドキュメント — https://docs.npmjs.com/cli/v10/commands/npm-exec — CI 環境での --yes 自動適用、--no フラグ、バージョン指定の仕様 2 3 4

  2. pnpm 公式 Settings ページ — https://pnpm.io/settingsminimumReleaseAge, trustPolicy, blockExoticSubdeps の仕様 2

  3. npm 公式 npx コマンドドキュメント — https://docs.npmjs.com/cli/v10/commands/npx--no フラグの動作仕様 2

  4. Darcy Clarke (元 npm CLI Staff Engineering Manager) "The Massive Hole in the npm Ecosystem" — https://blog.vlt.sh/blog/the-massive-hole-in-the-npm-ecosystem — Manifest Confusion 脆弱性の技術的詳細と各パッケージマネージャーへの影響 2 3 4

  5. pnpm 公式 pnpm dlx ドキュメント — https://pnpm.io/cli/dlx--allow-build オプション、postinstall スクリプトのデフォルト挙動 2 3 4

npm / pnpm サプライチェーン攻撃対策ユーザー設定ガイド

Executive Summary

npm/pnpm のサプライチェーン攻撃とは、依存パッケージに悪意あるコードを混入させる攻撃手法であり、タイポスクワット(誤字を突いた偽パッケージ)、依存関係混乱攻撃(Dependency Confusion)、アカウント乗っ取りによる正規パッケージへの悪意ある版の公開の3つが主な攻撃経路となる1。 npm v10 と pnpm v10 はいずれもこれらの攻撃に対抗するための設定オプションを豊富に提供しており、ユーザー側の設定変更だけで攻撃面を大幅に削減できる。 特に pnpm v10 は minimumReleaseAgetrustPolicyblockExoticSubdeps といったnpm にはない先進的なセキュリティ設定を備えており、積極的な採用価値がある2


1. サプライチェーン攻撃の主な種類

攻撃種別 概要 主な対策
タイポスクワット lodash1odash のように誰かが誤インストールを狙う スコープパッケージ化・監査
依存関係混乱 (Dependency Confusion) 社内プライベートパッケージと同名パブリックパッケージを公開し優先解決させる スコープ強制・プライベートレジストリ設定
アカウント乗っ取り メンテナーのパスワード侵害・期限切れドメイン再登録でアカウント奪取 2FA 強制・trustPolicy
ポストインストールスクリプト悪用 postinstall フックで任意コードを実行 ignore-scripts
悪意あるバージョン公開 既存の正規パッケージに悪意あるバージョンを追加 minimumReleaseAgetrustPolicy
エキゾチックソース経由の混入 git URL / 直接 tarball URL で悪意あるコードを間接依存に混入 blockExoticSubdeps

2. npm セキュリティ設定

設定ファイルの優先順位(高→低):

  1. コマンドラインフラグ
  2. 環境変数 (NPM_CONFIG_*)
  3. プロジェクト .npmrc (<project>/.npmrc)
  4. ユーザー .npmrc (~/.npmrc)
  5. グローバル .npmrc ($PREFIX/etc/npmrc)

2-1. ポストインストールスクリプトの無効化

# ~/.npmrc または <project>/.npmrc
ignore-scripts=true

ignore-scriptstrue にすると、パッケージの preinstall / install / postinstall / prepare スクリプトが実行されなくなる3。ほぼすべての悪意あるコードはこのスクリプト経由で実行されるため、最も効果的な設定の一つ。

⚠️ 副作用: ネイティブモジュール (node-gyp 等) のビルドも無効化される。node-sass など C++ アドオンを使うプロジェクトでは個別判断が必要。

2-2. 脆弱性監査の有効化と閾値設定

# ~/.npmrc
audit=true
audit-level=moderate
  • audit=true (デフォルト): npm install 時に自動でセキュリティ脆弱性チェックを実行3
  • audit-level: info / low / moderate / high / critical のいずれかを指定。指定レベル以上の脆弱性があった場合に非ゼロ終了コードを返す。CI で audit-level=high 以上を設定して強制ブロックするのが推奨4

2-3. パッケージ署名・来歴(Provenance)の検証

# インストール済みパッケージの署名を検証
npm audit signatures

npm v9.5.0 以降は npm audit signatures コマンドでレジストリ署名と SLSA プロベナンス証明を検証できる5。証明は Sigstore/Rekor に記録される改ざん防止ログで管理されており、公開後に内容を書き換えることは不可能。

2-4. ロックファイルの強制

# ~/.npmrc
package-lock=true

package-lock.json がなければインストールを失敗させる設定は直接存在しないが、CI では以下を利用:

npm ci  # package-lock.json が必須・完全一致でインストール

npm ci はロックファイルと package.json の差異があれば即エラーで終了するため、サプライチェーン攻撃の第一道線として機能する。

2-5. 厳密なバージョン固定

# ~/.npmrc
save-exact=true

save-exact=true を設定すると、npm install <pkg> 時に ^1.2.3 でなく 1.2.3 として package.json に記録される。SemVer の範囲解決を介した予期せぬバージョン更新を防ぐ3

2-6. レジストリとスコープの設定

# ~/.npmrc または <project>/.npmrc

# プライベートスコープをプライベートレジストリに向ける(依存関係混乱対策)
@myorg:registry=https://nexus.mycompany.com/npm/
//nexus.mycompany.com/npm/:_authToken=${NPM_PRIVATE_TOKEN}

# 公開レジストリは念のためデフォルトのまま明示
registry=https://registry.npmjs.org/

スコープパッケージ (@myorg/*) を使い、プライベートレジストリに向けることで Dependency Confusion 攻撃を根本的に防ぐ1。同名のパブリックパッケージが存在してもスコープが一致しない限り解決されない。

2-7. SSL 検証の強制

# ~/.npmrc
strict-ssl=true

デフォルトは true だが、開発環境で誤って false に設定している場合は MITM 攻撃に脆弱になる。必ず true を確認すること3

2-8. npm アカウントの 2FA 設定

# 書き込み操作(publish 等)にも 2FA を要求する
npm profile enable-2fa auth-and-writes

アカウント乗っ取りの最大の防壁。auth-and-writes モードでは認証だけでなく npm publishnpm owner add 等の変更操作にも 2FA が要求される6


3. pnpm セキュリティ設定

pnpm の設定は以下の2ファイルに分かれる:

  • pnpm-workspace.yaml: プロジェクトレベルのセキュリティ設定(pnpm 独自)
  • .npmrc: 認証・レジストリ設定(npm と共通形式)

3-1. 新規公開パッケージの遅延インストール(★pnpm 独自★)

# pnpm-workspace.yaml
minimumReleaseAge: 1440  # 1440分 = 24時間

悪意ある公開バージョンはほとんどの場合、公開から1時間以内に発見・削除される。minimumReleaseAge を設定することで、まだ審査されていない最新版が自動インストールされるリスクを排除できる2

特定パッケージを除外する場合:

minimumReleaseAge: 1440
minimumReleaseAgeExclude:
  - '@myorg/*'   # 自社パッケージは除外
  - react        # メジャーフレームワークも除外可

3-2. 信頼レベルの降格を禁止(★pnpm 独自★)

# pnpm-workspace.yaml
trustPolicy: no-downgrade

no-downgrade に設定すると、過去のバージョンがより高い信頼レベル(署名付き・プロベナンス付き)で公開されていたパッケージの、信頼レベルが低い新バージョンのインストールを失敗させる2。アカウント乗っ取り後に credentials を持たない攻撃者が悪意ある版を公開した場合に有効。

例外パッケージを設定:

trustPolicy: no-downgrade
trustPolicyExclude:
  - 'chokidar@4.0.3'
  - 'webpack@4.47.0 || 5.102.1'

古いパッケージ(署名機能導入前のバージョン)がブロックされる場合は trustPolicyIgnoreAfter を使用:

trustPolicy: no-downgrade
trustPolicyIgnoreAfter: 525600  # 1年以上前のパッケージは信頼ポリシー免除

3-3. 間接依存のエキゾチックソース禁止(★pnpm 独自★)

# pnpm-workspace.yaml
blockExoticSubdeps: true

true に設定すると、直接依存(package.json に記載)のみが git URL / tarball URL などのエキゾチックソースを使用できる。間接依存はレジストリ・ローカルパス・ワークスペースリンクのみに制限される2

エキゾチックソースの例:

  • git+ssh://git@github.com/evil/package
  • https://evil.com/malware.tgz

3-4. ロックファイルの厳密運用

# CI 環境での推奨(pnpm-lock.yaml が必須・更新不可)
pnpm install --frozen-lockfile

pnpm は CI 環境 (環境変数 CI=true 等) では自動的に --frozen-lockfile 相当の動作になる7。明示的にローカルでも有効にする場合:

# pnpm-workspace.yaml
frozenLockfile: true

3-5. スクリプト実行の無効化

pnpm install --ignore-scripts

または .npmrc に:

ignore-scripts=true

npm と同様に postinstall スクリプトを無効化する7

3-6. 脆弱性監査

pnpm audit --audit-level=high

特定の CVE を無視する場合は pnpm-workspace.yaml で:

auditConfig:
  ignoreCves:
    - CVE-2022-36313
  ignoreGhsas:
    - GHSA-42xw-2xvc-qx8m

3-7. 依存関係の上書き(脆弱なバージョンの強制更新)

# pnpm-workspace.yaml
overrides:
  "lodash@<4.17.21": "4.17.21"  # 脆弱バージョンを強制更新
  "foo@1.0.0>bar": "-"           # 不要な依存を除去

overrides は間接依存を含む全依存グラフに対して特定バージョンを強制できる。脆弱なバージョンが direct で更新できない場合の回避策として有効8


4. 設定比較表

設定項目 npm pnpm 効果
ignore-scripts .npmrc .npmrc postinstall スクリプト無効化
audit / audit-level .npmrc auditConfig in pnpm-workspace.yaml 既知脆弱性の検出・ブロック
npm audit signatures / pnpm audit コマンド コマンド パッケージ署名検証
save-exact .npmrc .npmrc バージョン完全固定
strict-ssl .npmrc .npmrc SSL 検証強制
スコープ別レジストリ .npmrc .npmrc Dependency Confusion 対策
ロックファイル強制 npm ci --frozen-lockfile 再現性保証
minimumReleaseAge ❌ なし pnpm-workspace.yaml 新規公開版の遅延インストール
trustPolicy ❌ なし pnpm-workspace.yaml 信頼レベル降格の禁止
blockExoticSubdeps ❌ なし pnpm-workspace.yaml 間接依存のソース制限
2FA (publish) npm profile enable-2fa npm 互換 アカウント乗っ取り防止

5. 推奨設定テンプレート

npm 向け (~/.npmrc または プロジェクト .npmrc)

# セキュリティ基本設定
ignore-scripts=false          # 必要なら true (ネイティブモジュール注意)
audit=true
audit-level=high
strict-ssl=true
save-exact=true

# スコープのプライベートレジストリ設定(Dependency Confusion 対策)
@myorg:registry=https://your-private-registry.example.com/
//your-private-registry.example.com/:_authToken=${NPM_TOKEN}
# CI パイプライン
npm ci                        # ロックファイル必須インストール
npm audit --audit-level=high  # 高深刻度以上でブロック
npm audit signatures          # 署名検証(npm v9.5.0+)

pnpm 向け (pnpm-workspace.yaml)

# サプライチェーン攻撃対策セキュリティ設定

# 新規公開バージョンは24時間待ってからインストール
minimumReleaseAge: 1440

# 信頼レベルの降格を禁止
trustPolicy: no-downgrade

# 間接依存の非レジストリソースを禁止
blockExoticSubdeps: true

# 脆弱性監査設定
auditConfig:
  ignoreCves: []

# 脆弱な間接依存の強制更新例
overrides:
  # "vulnerable-pkg@<2.0.0": "^2.0.0"
# CI パイプライン
pnpm install --frozen-lockfile  # ロックファイル必須
pnpm audit --audit-level=high   # 高深刻度以上でブロック

6. 追加の組織・CI レベル対策

  1. Dependabot / Renovate Bot の有効化: 自動的に依存関係のセキュリティアップデート PR を作成。
  2. npm Publish 時のプロベナンス有効化: GitHub Actions から publish する際に --provenance フラグを使用し、SLSA 来歴を添付5
  3. .npmrc のコミット禁止: シークレットを含む場合は .gitignore に追加。
  4. パッケージの SBOM 生成: npm sbom --sbom-format cyclonedx で Software Bill of Materials を生成し、依存関係を可視化。
  5. Organization レベルの 2FA 強制: npmjs.com の Organization 設定で全メンバーに 2FA を必須化6

Confidence Assessment

項目 信頼度 根拠
npm 設定オプション (ignore-scripts, audit, save-exact 等) npm 公式ドキュメント (v10) より直接引用
pnpm minimumReleaseAge, trustPolicy, blockExoticSubdeps pnpm 公式ドキュメント Settings ページより直接引用
npm Provenance / npm audit signatures GitHub Blog (2023) および npm 公式ドキュメントより
攻撃手法の分類 npm 公式脅威モデルページより
設定の副作用に関する注意 公式ドキュメント記載 + 一般的な運用知見に基づく推論

Footnotes

Footnotes

  1. npm 公式脅威モデルページ "Threats and Mitigations" — https://docs.npmjs.com/threats-and-mitigations — Typosquatting/Dependency Confusion 攻撃の説明と対策 2

  2. pnpm 公式 Settings ページ — https://pnpm.io/settingsminimumReleaseAge, trustPolicy, blockExoticSubdeps の仕様と解説 2 3 4

  3. npm 公式設定リファレンス — https://docs.npmjs.com/cli/v10/using-npm/configignore-scripts, audit, audit-level, save-exact, strict-ssl の仕様 2 3 4

  4. npm 公式 npm audit コマンドページ — https://docs.npmjs.com/cli/v10/commands/npm-audit--audit-level の CI での利用方法

  5. GitHub Blog "Introducing npm package provenance" (2023-02-02) — https://github.blog/2023-02-02-introducing-npm-package-provenance/ — SLSA プロベナンスの仕組みと npm audit signatures の解説 2

  6. npm 公式 2FA 設定ページ — https://docs.npmjs.com/configuring-two-factor-authenticationauth-and-writes モードの設定手順 2

  7. pnpm 公式 pnpm install コマンドページ — https://pnpm.io/cli/install--frozen-lockfile, --ignore-scripts オプションの仕様 2

  8. pnpm 公式 pnpm audit コマンドページ — https://pnpm.io/cli/auditoverrides を使った脆弱バージョン強制更新の手順

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment