## 経緯
昨今は[[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>