## 経緯 昨今は[[npmレジストリ]]に対する数々の攻撃が激化しており、自己防衛する必要があるため。主に[[サプライチェーン攻撃]]への対策。 ## スコープ - 開発者として最低限必要なこと - ライブラリ開発者のアクションはスコープ外 ### 対象[[パッケージマネージャー]] 普段使っている以下4つとする。 - [[npm]] - [[pnpm]] - [[Bun]] - [[Deno]] | 対象 | 2025/10 | 2026/04 | 2026/05 | | -------- | ------- | ------- | ------- | | [[npm]] | 11.6.1 | 11.11.0 | 11.12.1 | | [[pnpm]] | 10.22.0 | 10.26.0 | 11.0.0 | | [[Bun]] | 1.3.2 | 1.3.11 | 1.3.13 | | [[Deno]] | 2.5.1 | 2.7.12 | 2.7.13 | ## 1. 依存関係のバージョンをピン留めする 依存関係のバージョンを正確な値にする設定。[[セマンティックバージョニング]]の幅をもたせる表現はしないことで、マイナーバージョンアップやパッチバージョンアップによる不正なライブラリバージョンのインストールリスクを軽減できる。 ### [[直接依存関係]]のバージョンを固定する [[Deno]]以外は設定ファイルに記述できるので、設定ファイル推奨。 > [!info]- [[Deno]]以外でコマンド指定したい場合 > ```bash > npm install --save-exact <package> > pnpm add --save-exact <package> > bun add --exact <package> > ``` #### [[npm]] ```console npm config set save-exact=true ``` `~/.npmrc` に設定が追加される。 #### [[pnpm]] ```console pnpm config set save-exact true ``` `~/Library/Preferences/pnpm/config.yaml` に設定が追加される。 > [!hint]- [[pnpm]] < v11 の場合 > - [[#npm]]の設定がされていれば対応不要 (そちらが読み込まれる) > - [[#npm]]の設定をしていない場合は同様に `pnpm config set save-exact true` > - ただし、設定ファイルパスは `~/Library/Preferences/pnpm/rc` になる #### [[Bun]] [[#npm]]の設定がされていれば対応不要。(そちらが読み込まれる) [[npm]]の設定がされていない場合は、[[bunfig.toml]] に以下を追加する。 ```toml [install] exact = true ``` > [!caution] > **[[#npm]]の設定の方が [[bunfig.toml]]より優先される** ので注意。 #### [[Deno]] **設定はできない。** インストールコマンド([[deno add]]や[[deno install]])を実行するたびに、[[--save-exact (Deno)|--save-exact]]の指定が必要。 ```console deno add --save-exact <package> ``` > [!hint]- [[Deno]] < v2.7 の場合 > [[--save-exact (Deno)|--save-exact]]が未対応なのでバージョン指定してインストールする。以下は一例。 > ```console > deno add npm:[email protected] > ``` ### [[推移的依存関係]]のバージョンを固定する [[推移的依存関係]]のバージョンを固定する方法。 #### [[npm]] [[package.json]] に [[overrides (npm)|overrides]] を追加する。 ```json { "dependencies": { "owlelia": "0.49.0" }, "overrides": { "dayjs": "1.11.18" } } ``` その後に [[npm install]] すると、[[推移的依存関係]]の[[dayjs]]は `1.11.18` に固定される。 ```console $ npm i owlelia $ npm ls --depth=2 └─┬ [email protected] └── [email protected] overridden ``` #### [[pnpm]] [[pnpm-workspace.yaml]] に [[overrides (pnpm)|overrides]] を追加。 ```yaml overrides: "dayjs": "1.11.18" ``` その後に [[pnpm install]] すると、[[推移的依存関係]]の[[dayjs]]は `1.11.18` に固定される。 ```console $ pnpm i $ pnpm ls --depth=2 └─┬ [email protected] └── [email protected] ``` #### [[Bun]] [[package.json]] に [[overrides (npm)|overrides]] を追加する。 ```json { "dependencies": { "owlelia": "0.49.0" }, "overrides": { "dayjs": "1.11.18" } } ``` その後に [[bun install]] すると、[[推移的依存関係]]の[[dayjs]]は `1.11.18` に固定される。 ```console $ bun i $ bun list --all ├── [email protected] └── [email protected] ``` > [Overrides and resolutions - Bun](https://bun.com/docs/install/overrides) #### [[Deno]] [[package.json]] に [[overrides (npm)|overrides]] を追加する。 ```json { "overrides": { "dayjs": "1.11.18" } } ``` その後に [[deno install]] すると、[[推移的依存関係]]の[[dayjs]]は `1.11.18` に固定される。 ```console $ deno i $ cat deno.lock | grep dayjs "[email protected]": { "dayjs" ``` > [!hint]- [[Deno]] < v2.7 の場合は未対応 > [Deno 2.7: Temporal API, Windows ARM, and npm overrides | Deno](https://deno.com/blog/v2.7#packagejson-overrides-support) > [!question]- [[deno.json]]に設定を追加できないのか? > v2.7.13時点では未サポート。[[Deno]]に関係ないものは[[package.json]]に書くポリシーらしい。 ## 2. ロックファイルを管理してインストールに利用する [[package.json]]などの依存関係記述ファイルと、[[package-lock.json]]などのロックファイルに齟齬がある場合はエラーになるインストール方法。 パッケージバージョン指定がちゃんとできていなかったことによる、危険なバージョンのライブラリインストールリスクを軽減できる。 ### [[npm]] [[npm ci]] コマンドを実行する。 ```console npm ci ``` 主な違いは以下。[[pnpm]]、[[Bun]]も基本的な思想は同じ。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" /> <span class="link-card-v2-site-name">Minerva</span> </div> <div class="link-card-v2-title"> npm installとnpm ciの違い </div> <div class="link-card-v2-content">npm ciがnpm installと異なる点は以下5点。</div> <img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/glossary.webp" /> <a data-href="npm installとnpm ciの違い" class="internal-link"></a> </div> %%[[npm installとnpm ciの違い]]%% ### [[pnpm]] [[pnpm ci]] コマンドを実行する。 ```console pnpm ci ``` > [!hint]- [[pnpm]] < v11 の場合 > [[pnpm ci]]コマンドが存在しないので、[[--frozen-lockfile (pnpm install)|--frozen-lockfile]]オプションを指定して [[pnpm install]] コマンドを実行する。 > > ```console > pnpm i --frozen-lockfile > ``` ### [[Bun]] [[bun ci]] コマンドを実行する。 ```console bun ci ``` ### [[Deno]] [[--frozen (Deno)|--frozen]]オプションをつけて [[deno install]] コマンドや [[deno run]] コマンドを実行する。 ```console deno install --frozen ``` ## 3. ライフサイクルスクリプトを無効にする `pre*` `post*` のような[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]を無効化する方法。インストール時にローカルマシンへ攻撃されるリスクを軽減できる。 ここでは設定方法だけを記載するので、設定が効いているかの確認は以下を参照。 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/favicon-64.png" /> <span class="link-card-v2-site-name">Minerva</span> </div> <div class="link-card-v2-title"> 📕ライフサイクルスクリプトが実行されているかを確認する </div> <div class="link-card-v2-content">npm・pnpm・Bun・Denoでpostinstallなどライフサイクルスクリプトの有効無効を設定し、実行ログや警告から確認する方法を比較解説する。</div> <img class="link-card-v2-image" src="https://publish-01.obsidian.md/access/35d05cd1bf5cc500e11cc8ba57daaf88/Notes/attachments/prime.webp" /> <a data-href="📕ライフサイクルスクリプトが実行されているかを確認する" class="internal-link"></a> </div> %%[[📕ライフサイクルスクリプトが実行されているかを確認する]]%% ### [[npm]] 以下の設定で、**ルートプロジェクトと依存関係含めたすべての**[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]を無効化できる。 ```console npm config set ignore-scripts true ``` [[npmrc|.npmrc]] に以下が追加される。 ``` ignore-scripts=true ``` [[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]が無視された場合でもエラーにはならない。 ### [[pnpm]] **✅ デフォルトの設定でOK**。 v11では[[strictDepBuilds (pnpm)|strictDepBuilds]]がデフォルトで `true` なため、**ルートプロジェクト以外の**[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]が実行されるとエラーで終了する。 依存関係のインストールに[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]が必要な場合は、[[pnpm-workspace.yaml]]に[[allowBuilds (pnpm)|allowBuilds]]を追加する。 ```yaml allowBuilds: hogePackage: true fugaPackage: true ``` > [!hint]- [[pnpm]] < v11 の場合 > [[ignoreScripts (pnpm)|ignoreScripts]]を有効にし、**ルートプロジェクトと依存関係含めたすべての**[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]を無効化する。 > > ```console > pnpm config set ignore-scripts true > ``` > > `pnpm/rc` に以下が追加される。 > > ``` > ignore-scripts=true > ``` > > - [[npm]]の設定をしていれば対応不要 > > > [!hint] デフォルトの挙動 > > [[pnpm]]がv10以上であれば、ルートプロジェクト以外の[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]は無効になる。 > [!hint]- ルートプロジェクトの[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]も無視したい場合 > [[ignoreScripts (pnpm)|ignoreScripts]]を `true` に設定する。 ### [[Bun]] **✅ デフォルトの設定でOK**。 デフォルトで依存関係の[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]は原則無効化されている。ただし [[trustedDependencies (Bun)|trustedDependencies]] や [[default-trusted-dependencies (Bun)|default-trusted-dependencies]] は例外。 > [!hint]- ルートプロジェクトの[[ライフサイクルスクリプト (npm)|ライフサイクルスクリプト]]も無視したい場合 > [[bunfig.toml]]にv1.2.0で追加された[[install.ignoreScripts (Bun)|install.ignoreScripts]]を設定する。 > > ```toml > [install] > ignoreScripts = true > ``` > > - [[npm]]の設定がされている場合はそちらが反映されるので設定不要。 > > > [!hint]- v1.2.0 より前のバージョンの場合 > > [[--ignore-scripts (Bun)|--ignore-scripts]] オプションを使う。設定はできない。 > > > > ```console > > bun install --ignore-scripts > > ``` ### [[Deno]] **✅ デフォルトの設定でOK**。 [[Denoのライフサイクルスクリプトはデフォルトで無効]]のため、[[deno approve-scripts]]コマンドを使って[[allowScripts (Deno)|allowScripts]]に追加する。[[Prisma]]の場合は以下。 ```json { "allowScripts": ["npm:@prisma/[email protected]", "npm:[email protected]"] } ``` ## 4. Minimal Release Ageの指定 新しいパッケージバージョンが更新されてから、インストールできるようになるまでのラグを意図的に設定する。[[サプライチェーン攻撃]]によって不正化したライブラリがインストールされるリスクを軽減できる。(数日経てば対策がとられることも多いため) - 動作は未確認 - 特定ライブラリだけすぐにアップデートしたい場合はexclude系の設定を利用する ### [[npm]] v11.10.0で追加された [[min release age (npm)|min release age]] を設定する。 ```console # 1日のラグを設ける npm config set min-release-age 1 ``` `.npmrc` に以下が追加される。 ``` min-release-age=1 ``` > [!hint]- v11.10.0 より前のバージョンの場合 > インストール時に `--before` オプションで日付を指定する。 > > ```bash > # macOSの場合. 1週間ラグを設ける > npm install --before="$(date -v -7d)" > ``` ### [[pnpm]] **✅ デフォルトの設定でOK**。 v11ではデフォルトで最適化されている。 | 設定 | default | 意味合い | | ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------- | | [[minimumReleaseAge (pnpm)\|minimumReleaseAge]] | `1440` (1日) | リリースされたパッケージは1日経過しないとインストールされない。 | | [[minimumReleaseAgeStrict (pnpm)\|minimumReleaseAgeStrict]] | `false` | 要求範囲内に条件を満たすバージョンがない一部ケースでは、条件未満のバージョンへフォールバックしてインストールを許容する。新規依存関係の追加時など。 | > [!hint]- [[pnpm]] < v11 の場合 `OR` 1日以外の値を設定したい場合 > v10.16で追加された [[minimumReleaseAge (pnpm)|minimumReleaseAge]] を設定する。 > > ```bash > # 1日のラグを設ける(分設定) > pnpm config set minimumReleaseAge 1440 > ``` > > v11以上だと `pnpm/config.yaml` や [[pnpm-workspace.yaml]] に以下が追加される。 > > ```yaml > minimumReleaseAge: 1440 > ``` > > v10以下だと `pnpm/rc` に以下が追加される。 > > ``` > minimum-release-age=1440 > ``` > [!hint]- プロジェクト単位の設定 > ```console > pnpm config set --location=project minimumReleaseAge 1440 > ``` > [Settings (pnpm-workspace.yaml) > minimumReleaseAge | pnpm](https://pnpm.io/settings#minimumreleaseage) ### [[Bun]] v1.3で追加された[[minimumReleaseAge (Bun)|minimumReleaseAge]]を設定する。 `bunfig.toml` ```toml [install] minimumReleaseAge = 86400 # 1日のラグを設ける(秒設定) ``` > [bun install > Minimum release age - Bun](https://bun.com/docs/pm/cli/install#minimum-release-age) ### [[Deno]] v2.5.5で対応された[[minimumDependencyAge (Deno)|minimumDependencyAge]]を使う。 `deno.json` ```json { // 1日のラグを設ける(分設定) "minimumDependencyAge": 1440 } ``` > [feat(unstable): ability to specify minimum dependency age in deno.json file by dsherret · Pull Request #31007 · denoland/deno](https://github.com/denoland/deno/pull/31007) ## 5. [[推移的依存関係]]で危険なソース指定方法を禁止する [[サプライチェーン攻撃]]の対象となったパッケージの依存関係を書き換え、[[Git]]リポジトリやtarball経由で悪性スクリプトを取り込む攻撃から防御するため。 ### [[npm]] #2026/04/26 時点ではなさそう。 ### [[pnpm]] **✅ デフォルトの設定でOK**。 v11ではデフォルトで最適化されている。 | 設定 | default | 意味合い | | ----------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------- | | [[blockExoticSubdeps (pnpm)\|blockExoticSubdeps]] | `true` | [[推移的依存関係]]に信頼できないソースのプロトコルを許容しない。 | > [!hint]- [[pnpm]] < v11 の場合 > v10.26.0 で追加された[[blockExoticSubdeps (pnpm)|blockExoticSubdeps]]を設定する。 > > ```console > pnpm config set blockExoticSubdeps true > ``` > > `pnpm/rc` に以下が追加される。 > > ``` > block-exotic-subdeps=true > ``` ### [[Bun]] #2026/04/26 時点ではなさそう。 ### [[Deno]] #2026/04/26 時点ではなさそう。 ## 参考 <div class="link-card-v2"> <div class="link-card-v2-site"> <img class="link-card-v2-site-icon" src="https://github.githubassets.com/favicons/favicon.svg" /> <span class="link-card-v2-site-name">GitHub</span> </div> <div class="link-card-v2-title"> GitHub - bodadotsh/npm-security-best-practices: How to stay safe from NPM supply chain attacks </div> <div class="link-card-v2-content"> How to stay safe from NPM supply chain attacks. Contribute to bodadotsh/npm-security-best-practices development ... </div> <img class="link-card-v2-image" src="https://opengraph.githubassets.com/5c06c0762731d936cb4e3f089a6310518e1801910f234f41b2e73d8b27f0ce51/bodadotsh/npm-security-best-practices" /> <a href="https://github.com/bodadotsh/npm-security-best-practices"></a> </div>