供給チェーン攻撃防御におけるcooldownの効果と限界

2026-05-17 hit count image

Dependabot cooldownやパッケージマネージャーのminimum release ageのような時間ベースの 供給チェーン防御戦略が実際にどれほど有効かを、これまでのインシデントデータで検証します。

web

はじめに

第1回axiosの事例を通じて供給チェーン攻撃がどのように機能するかを確認し、第2回で実際のプロジェクトに適用した3つの防御戦略を解説しました。3つの戦略に共通する原則は一言で要約できます。

新しく公開されたものをすぐに受け入れない。

この記事で検証したい問いは2つです。

  1. このシンプルな原則は実際にどれほど有効か?
  2. そして何を防げないのか?

攻撃の時間的パターン

供給チェーン攻撃が時間ベースの防御で防げるという主張は、次の観察から出発します。

悪意あるパッケージが公開されてから検出・削除されるまでの時間は、ほぼ常に数時間から数日以内に収まる。

このパターンが成立する理由は次のとおりです。

  • 人気パッケージほど自動セキュリティスキャナー(SocketSnykGitHub Advisoryなど)が即座に分析
  • 不審な動作を発見した開発者がすぐにIssueを起票
  • メンテナー本人またはnpm運営側が発見次第パッケージをunpublish

つまり攻撃者には、悪意あるバージョンがレジストリに生きている時間 — 公開された瞬間から検出・削除されるまでの短い期間 — の間だけ攻撃の機会が与えられます。この期間をセキュリティ分野では露出ウィンドウ(window of exposure)と呼び、供給チェーン攻撃ではこの期間が通常非常に短いです。数日も経てば悪意あるバージョンはレジストリから消え、その間に新しい正規バージョンが公開されています。新バージョンを数日遅らせて受け取ると決めた人は、この露出ウィンドウを自然に回避できます。

既知インシデントの露出ウィンドウ

代表的なnpm供給チェーンインシデントの露出ウィンドウをまとめると次のとおりです。

インシデント時期露出ウィンドウ(公開→削除)
ua-parser-js2021約4時間
Ledger Connect Kit2023約5時間
Solana web3.js2024約5時間
axios2026約4時間

このいずれも24時間を超えていません。つまり、たった1日待つだけで、上記インシデントの悪意あるバージョンを実際のプロダクト環境に取り込まずに済みました。

供給チェーン防御におけるcooldown期間

先に見たように供給チェーン攻撃の露出ウィンドウは通常非常に短いため、「新バージョンを数日遅らせて受け取る」というシンプルなゲートだけで相当な防御効果が得られます。では、そのゲート期間は何日に設定すべきでしょうか?

このゲート期間はツールによって呼び方が異なります。

  • Dependabot: cooldown
  • pnpm · Renovate · Yarn: minimumReleaseAge(またはnpmMinimalAgeGate
  • bun · uv: それぞれminimumReleaseAgeexclude-newer

呼び方は違っても本質は同じです — 「公開されてからN日が経過していない新バージョンはインストール・PR作成の対象から除外する」。そしてこのNの業界推奨値はほぼ一貫して7日前後に収束しています。

このセクションでは、その7日という値を根拠 → 推奨 → 副作用の3つの観点から順番に見ていきます。

1. 根拠: 主要ツールとセキュリティ分析が収束する値

7日は任意に選んだ数字ではなく、実際に複数のパッケージマネージャーとセキュリティ研究・ツールが同様に採用・推奨している値です。

出典推奨・デフォルト値備考
GitHub Dependabot cooldown公式ドキュメントサンプル設定で7日公式referenceの推奨例
pnpm minimumReleaseAge (v11)デフォルト1日後方互換を考慮した保守的なデフォルト値。ガイドはより長い期間を推奨
pnpm供給チェーンセキュリティガイド7日公開後7日未満のバージョンのインストールを強く非推奨
Renovate minimum-release-ageガイドを提供公式キーコンセプトドキュメントに掲載
Socket7日フラグ公開後7日未満のバージョンを自動でリスクシグナルとして表示

GitHub公式ドキュメント、主要パッケージマネージャー(pnpm・Renovate)、そして供給チェーンセキュリティSaaS(Socket)がいずれも7日前後を「短命な悪意ある公開(short-lived malicious publish)をフィルタリングするのに十分な期間」と見ている点が重要です。

2. 推奨: 7日を出発点として設定する

総合すると推奨は次のとおりです。

新規導入プロジェクトであれば7日を出発点として設定する。

7日が標準的な推奨値として定着した理由は、1日や30日と比較すると明確です。

  • 1日では不十分です。利用者が少ないパッケージ・休日・週末の公開・メンテナーの休暇などにより検出が遅れる場合があります。1日は核心的な4〜5時間のインシデントをフィルタリングできる程度です。
  • 30日は過剰です。ブロックできるインシデントは若干増えますが、その分新機能・機能改善の導入が遅れます。また依存関係lockfileの鮮度が落ちて別の運用コストが生まれます。
  • 7日はその中間の合意点です。ほぼすべての短命な悪意ある公開をフィルタリングしながら、正当な新バージョンを1週間以内に受け取ることができます。

これを実際のインシデントデータで検証すると、既知の主要供給チェーンインシデント21件のうち、7日ゲートでブロック可能なインシデントは11件です。半数以上をシンプルな設定1行で防げるということです。

インシデント時期エコシステム露出ウィンドウ7日ゲートでブロック
axios2026npm約4時間ブロック可能
Trivy-Action2026GitHub Actions約12時間ブロック可能
Nx / S1ngularity2025npm約1日ブロック可能
Gluestack / React Native ARIA2025npm数日ブロック可能
tj-actions/changed-files2025GitHub Actions約3日ブロック可能
reviewdog/action-setup2025GitHub Actions約2時間ブロック可能
Ultralytics YOLO2024PyPI約1〜2日ブロック可能
Solana web3.js2024npm約5時間ブロック可能
Polyfill.io2024CDN約4か月ブロック不可
XZ Utils2024OSパッケージ2年以上ブロック不可
Ledger Connect Kit2023npm約5時間ブロック可能
3CX2023デスクトップアプリ数週間ブロック不可
PyTorch torchtriton2022PyPI約5日ブロック可能
node-ipc2022npm数週間以上ブロック不可
colors / faker2022npm数日〜数週間部分ブロック
Log4Shell2021Java該当なしブロック不可
ua-parser-js2021npm約4時間ブロック可能
Codecov2021SaaS約2か月ブロック不可
Dependency Confusion (Alex Birsan)2021複数ケースごとに異なる部分ブロック
SolarWinds2020ビルドシステム約9か月ブロック不可
event-stream2018npm約2か月ブロック不可

部分ブロックは攻撃の一部の段階だけ防げる場合(例:最初の数日間の露出は防ぐが、それ以降の段階は通過)を意味します。単純集計ではブロック可能11件 / ブロック不可8件 / 部分ブロック2件です。

表からもう1つ読み取れる事実は、ブロック不可のインシデントはいずれも7日ゲートの本質的な限界と直結しているという点です。XZ Utils・SolarWinds・event-streamのように露出期間が数か月から数年に及ぶ長期潜入、Polyfill.io・Codecovのようにnpmレジストリ外部で発生した攻撃、Log4Shellのような正常コードの欠陥は、いずれもcooldownでは防げない領域です。これらの限界は後述のどんな攻撃は防げないかで詳しく解説します。

14日・30日に延ばせばブロックできるインシデントが若干増えますが、その分正当なセキュリティパッチを受け取るのも遅くなるというコストが伴います。7日はそのバランスが比較的うまく取れている点であることが、現在の業界のコンセンサスに近いです。

どんな攻撃を防げるか

7日ゲートは次のタイプに効果的です。

1. メンテナーアカウント乗っ取り型

最も多く、最も早く検出されるタイプです。第1回axios事例がこれに該当します。乗っ取り → 悪意ある公開 → 検出 → 削除までのサイクルが通常24時間以内に完結するため、7日ゲートでほぼすべてブロックできます。

2. タイポスクワッティング(typosquatting)

react-doomのような人気パッケージに似た名前の偽パッケージを作り、タイポによるインストールを狙う攻撃です。こうしたパッケージは公開直後に自動スキャナーに引っかかり、数日以内に削除されます。

3. 依存関係コンフュージョン(dependency confusion)

内部プライベートパッケージと同じ名前のパッケージを公開レジストリに公開し、ビルドシステムが誤ってそれを取得するよう誘導する攻撃です。これも発見次第削除されるパターンに従います。

4. 素早い依存関係注入型

axios事例のplain-crypto-jsのように、侵害されたパッケージが新しい悪意あるパッケージを新しい依存関係として追加する形です。重要なのは、その新しい依存関係自体が公開されたばかりの新規パッケージだという点です。7日ゲートは侵害されたメインパッケージの新バージョンも、それが引き込む新しい依存関係もいずれもブロックします。

どんな攻撃は防げないか

防御戦略を評価する際に重要な問いは「これが防げないのは何か」です。7日ゲートが無力なケースは次のとおりです。

1. 長期潜入型 — XZ Utils事例

2024年に発見されたXZ Utilsのバックドアは、攻撃者が2年かけてメンテナーとしての信頼を築いた後にバックドアを挿入した事例です。通常の正式リリースを通じてバックドアが入り込み、そのコードは数か月間誰にも疑われませんでした。

7日ゲートは「公開されたばかりのもの」を防ぎますが、公開から数か月が経過して正常と認められたバージョンに対しては無力です。

2. メンテナー本人による意図的なサボタージュ

colors.js(2022)とnode-ipc(2022)の事例のように、メンテナー本人が意図的に悪意あるコードを挿入する場合があります。この場合もコードは正式リリースの手順を経て公開されます。7日ゲートは最初の数日はブロックできますが、メンテナーが意図的にゆっくり進めれば、ゲートを通過した後に悪意あるコードが広まります。

3. ビルドシステムの侵害

SolarWinds(2020)、3CX(2023)のようなインシデントは、パッケージレジストリではなくビルドシステム自体が侵害されたケースです。正常なバージョン番号で正常な手順を通じて配布されましたが、ビルド成果物自体にバックドアが含まれていました。パッケージマネージャーレベルの防御はここには届きません。

4. レジストリ外部からの攻撃

Codecov(2021)はCI環境でダウンロードされるシェルスクリプトが改ざんされたインシデントで、Polyfill.io(2024)はCDNを通じて直接JavaScriptをロードしていたところ、ドメインが買収されて悪意あるコードが注入されたインシデントでした。いずれもnpmゲートとは無関係な経路で侵入します。

5. 合法的なコードに潜む通常の脆弱性

Log4Shellのように、悪意なく書かれた正規ライブラリにセキュリティの欠陥がある場合です。これは供給チェーン攻撃ではなく一般的な脆弱性であり、むしろ素早くパッチを受け取るのが正解です。7日ゲートはこのケースではむしろ欠点になります。

攻撃タイプ7日ゲートの効果
メンテナーアカウント乗っ取り非常に効果的
タイポスクワッティング非常に効果的
依存関係コンフュージョン非常に効果的
素早い依存関係注入非常に効果的
長期潜入(XZ Utils型)無力
メンテナーのサボタージュ限定的
ビルドシステムの侵害無力
レジストリ外部(CDN、スクリプト)無力
正規ライブラリの脆弱性むしろ欠点

防御のコスト

この防御が「シンプルで効果的」と評価される理由は、コストがほとんどかからないからです。

コスト項目レベル
設定変更量設定ファイル数行
学習コストほぼなし
CI/CDパイプラインの変更Yarn 4アップグレード時の--immutable置き換え程度
正当なパッチ受信の遅延平均7日(セキュリティアラートは即時)
運用負担ほぼなし(Dependabotが自動処理)

最大のコストは「正当な新バージョンを7日遅れで受け取ること」ですが、セキュリティアラートはcooldownの対象外であるため、実際には大きな問題になることはほとんどありません。

defense-in-depthの一手段として

重要なのは、この戦略が万能ではなく、そうあるべきでもないという点です。7日ゲートは素早い検出・削除パターンに合わせた時間ベースのフィルターに過ぎません。上で見たように長期潜入、ビルドシステムの侵害、レジストリ外部からの攻撃は別の防御手段が必要です。

ここで言うdefense-in-depth(多層防御)とは、1つの強力な防御だけに依存せず、性質の異なる複数の防御手段を重ねて運用するセキュリティ原則です。ある手段が防げなかった攻撃を次の手段が防ぐという形で、互いの弱点を補い合うように配置します。cooldownはその複数の手段のうちの1つに過ぎません。

cooldownと合わせて運用すると効果的な他の防御手段は次のとおりです。

防御手段防ぐ攻撃
enableScripts: falsepostinstallで実行される即時RCE
GitHub Actions SHAピン固定Actions自体の改ざん
Hardened Mode(Yarn 4)lockfileの改ざん
OIDC / signed publish非公式ルートからの公開
SBOM管理依存関係の可視性
自動セキュリティスキャナー既知の脆弱性・悪意あるパターンの検出
コードレビュー意図的なサボタージュ(依存関係変更への注意)
CDN完全性(SRI)レジストリ外部の資産改ざん

第2回で取り上げた3つの戦略は、この表の一部に相当します。残りは組織の状況に応じて段階的に導入できます。

まとめ

このシリーズをまとめると次のとおりです。

  • 第1回: npmの信頼モデルは構造的に攻撃にさらされており、axiosの事例は悪意あるバージョンが生きている露出ウィンドウが非常に短いながらも確実に存在することを示した。
  • 第2回: 実際のプロジェクト環境では、GitHub Actions SHAピン固定、Dependabot cooldown、Yarn npmMinimalAgeGateの3つでその露出ウィンドウを自然に回避できる。
  • 第3回(この記事): シンプルな時間ベースのゲートは既知の供給チェーンインシデントのかなりの数を防げるが、万能ではないため他の防御手段と合わせて運用する必要がある。

供給チェーンセキュリティは本質的に確実な一手がない領域です。すべての依存関係を自分で監査することもできないし、すべてのメンテナーの意図を検証することもできません。だからこそ「コストがほとんどかからないシンプルな防御」が輝きます。7日という数字1つに設定ファイル数行、それだけで既知の供給チェーン攻撃の半数以上を防げるという点は — かけた労力に対して得られる効果がこれほど大きい防御という意味で — セキュリティ領域ではなかなか見られない希少なケースです。

あなたのプロジェクトにまだcooldownやminimum age gateがなければ、今が導入する最良のタイミングです。

参考資料

私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!

アプリ広報

今見てるブログを作成たDekuが開発したアプリを使ってみてください。
Dekuが開発したアプリはFlutterで開発されています。

興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。



SHARE
Twitter Facebook RSS