uga.dev - A Front-end Engineer's shed

現在のテーマは「ライトモード」です。

パッケージマネージャーのサプライチェーン攻撃対策(npm / Yarn / pnpm / Bun)

最終更新日:公開日:

  • #Security
  • #JavaScript
  • #npm
  • #Yarn
  • #pnpm
  • #Bun

2026年3月、週間1億ダウンロードを超えるaxiosに悪意あるコードが混入しました。過去にもua-parser-js@solana/web3.jsなど、広く使われるパッケージが同様の被害を受けています。

主要なパッケージマネージャー(npm、Yarn、pnpm、Bun)や自動アップデートツール(Dependabot、Renovate)は、こうしたサプライチェーン攻撃への対策を整備してきました。

本記事では、2026年時点でのサプライチェーン攻撃対策についてまとめたいと思います[1]

1. パッケージのエイジゲート(`min-release-age`系)

主要なパッケージマネージャーには、公開から一定期間が経過していないバージョンをインストール対象から除外する設定が用意されています。

悪意のあるパッケージは公開から数時間〜数日以内に検出・削除されることがほとんどです[2]。この「検出までの空白期間」にインストールしてしまわないよう、どのパッケージマネージャーでも閾値を設定しておくことが一般的に有効とされています。

パッケージマネージャー

プロパティ名

単位

3日の場合

npm (v11.10+)

min-release-age

3

pnpm (v10.16+)

minimum-release-age

4320

Yarn (v4.10+)

npmMinimalAgeGate

string

3d

Bun (v1.3+)

minimumReleaseAge

259200

なぜ3日?

パッケージマネージャーにはアップデートの性質が緊急なのか軽微な修正なのかを判断する仕組みがありません。あまり長い期間に設定すると緊急のセキュリティパッチの適用が遅れるリスクや、バイパスして無理やりアップデートする手間が出てきてしまいます。

最短で考えると、前述の通り主要な攻撃は数時間で検出されているので24時間でもよさそうに感じます。

しかし、木曜日に公開された悪意のあるバージョンが金曜日に適用され、週末を挟んで月曜まで気づかれないというケースがありそうです。いや、週末にパッケージアップデートすな

そう考えると、3日が妥当に感じますね。

ちなみに後述でも触れますが、Renovateのconfig:best-practicesのプリセットでも3日がデフォルト値として採用されています[3]

2. ロックファイルを使おう

ロックファイルとは、npm installなどで解決された依存関係の正確なバージョンとハッシュを記録するファイルです。

これをリポジトリにコミットしておくことで、うっかり違うバージョンをインストールする事態を防げます。

パッケージマネージャー

ロックファイル

形式

npm

package-lock.json

JSON

Yarn

yarn.lock

独自形式

pnpm

pnpm-lock.yaml

YAML

Bun

bun.lock

JSONC

さらに、インストール時にロックファイル内に記録されたハッシュでパッケージを検証するため、レジストリ側でパッケージの中身が差し替えられた場合にも検知できます。

また、攻撃が発覚した際、汚染されたバージョンが自分のプロジェクトに含まれているかどうかを、ロックファイルを確認するだけですぐに判断できます。

CI環境では`--frozen-lockfile`系を使おう

CI環境やデプロイ時には、ロックファイルの内容を厳密に再現するコマンドを使いましょう。package.jsonと一致しなければCIが転けるため、意図しないバージョンの混入を防げます。

パッケージマネージャー

コマンド

npm

npm ci

Yarn

yarn install --immutable

pnpm

pnpm install --frozen-lockfile

Bun

bun install --frozen-lockfile

Yarn Classic(v1)では--frozen-lockfileだったが、Yarn v2以降ではimmutableになりました。統一して…。yarn ciでええんじゃないですかね。

3. `postinstall`の無効化

パッケージのインストール時に、サードパーティのスクリプトが自動実行されないようにすることも効果的です。先日のaxiosの件は、このpostinstallスクリプトが攻撃の起点でした。

  • npm.npmrcignore-scripts=true を追加する。

  • pnpm(v10以降)デフォルトで無効[4]

  • Bun(v1.2以降)デフォルトで無効[5]

  • Yarn Berry(v2以降)デフォルトで無効[6]

Postinstall scripts should be avoided at all cost, as they make installs slower and riskier. Many users will refuse to install dependencies that have postinstall scripts. Additionally, since the output isn't shown out of the box, using them to print a message to the user will not work as you expect.

引用:Lifecycle Scripts | Yarn

上記のようにYarnもpostinstallスクリプトの使用を強く非推奨としています。

パッケージインストール後にスクリプトを自動実行してくれるpostinstallは便利な側面もありますが、こういうリスクがあるのは恐ろしいですね。自分の目に見えない範囲では動かないようにしておくのが良さそうです[7]

4. アップデートツールにもエイジゲートを設定しよう

パッケージマネージャーの設定だけでは、DependabotやRenovateが生成するプルリクエスト経由で新しいバージョンが入り込む可能性があります。

こちらにも設定を追記しましょう。

Renovate: `minimumReleaseAge`

Renovateではアップデートの性質によってエイジゲートの日数を調整できるので、通常は3日、差分が大きいもの[8]については5日、セキュリティパッチ[9]vulnerabilityAlerts)については即時に設定しておくのが良さそうです。こうすることで、緊急度の高いセキュリティパッチは即座にPRが作成され、それ以外は指定した日数待機してからPRが作成されます。

renovate.json
{
  "minimumReleaseAge": "3 days",
  "packageRules": [
    {
      "matchUpdateTypes": ["major"],
      "minimumReleaseAge": "5 days"
    },
    {
      "matchUpdateTypes": ["minor"],
      "minimumReleaseAge": "5 days"
    }
  ],
  "vulnerabilityAlerts": {
    "minimumReleaseAge": "0 days"
  }
}

Dependabot: `cooldown`

cooldownブロックでアップデートの性質ごとにエイジゲートの日数を指定できます。セキュリティアップデートの場合はエイジゲートを無視するため、明示的に0を指定する必要はありません。

.github/dependabot.yml
version: 2
updates:
  - package-ecosystem: 'npm'
    directory: '/'
    schedule:
      interval: 'daily'
    cooldown:
      default-days: 3
      semver-major-days: 5
      semver-minor-days: 5

まとめ

  • min-release-ageで公開されてから時間が経ったパッケージだけを利用する

  • ロックファイルを厳格に管理し、意図しない変更を防ぐ

  • ignore-scriptspostinstall由来のコード実行をブロックする

  • アップデートツールにもエイジゲートを設ける

それぞれは簡単な設定変更ですが、サプライチェーン攻撃にたいして有意義な対策です。みなさんのプロジェクトもぜひ見直してみてください💡

ところでこのmin-release-ageの概念って日本語でなんていうのがいいんだろう。クールダウン?エイジゲートでいいのかな?

…年確?

…。

エイジゲートでいいか…。

謝辞

@mehm8128さんより、pnpm v10以降・Bun v1.2以降はデフォルトでpostinstallが無効化されている旨をご指摘いただき記事を修正しました。ありがとうございます!!🙏

脚注