なぜ誰もが依存関係クールダウンを使うべきなのか
(blog.yossarian.net)- 依存関係クールダウン(dependency cooldown) は、オープンソースのサプライチェーン攻撃の大半を緩和できる シンプルで効果的なセキュリティ手法
- 攻撃者は通常、人気のあるオープンソースプロジェクトを乗っ取って悪意あるコードを配布するが、ほとんどの攻撃は 露出期間が1週間以下 と短い
- 新バージョン公開後に一定期間(例: 7日)待機するクールダウンを設定すれば、自動更新による感染リスクを大幅に減らせる
- Dependabot、Renovate、pnpm などはすでに クールダウン機能を標準でサポート しており、設定も簡単で追加コストもない
- パッケージマネージャーのレベルでクールダウンを標準提供すれば、サプライチェーンセキュリティの強化と不要なアラートの削減 に貢献できる
サプライチェーン攻撃の構造と問題点
- ほとんどの サプライチェーン攻撃(supply chain attack) は同じパターンを持つ
- 攻撃者が人気オープンソースプロジェクトの 認証情報の窃取 または CI/CD の脆弱性 を利用してアクセス
- 悪意ある変更を配布チャネル(PyPI、npm など)にアップロード
- 自動更新やバージョン固定の不備により、ユーザーが感染したバージョンをインストール
- セキュリティベンダーがこれを検知して警告し、その後パッケージリポジトリが該当バージョンを削除
- (1)〜(2) 段階の間隔は長いが、(2)〜(5) 段階は 数時間〜数日以内に処理 されるため、攻撃者の活動期間は短い
- 最近18か月間の主要事例における 攻撃可能期間(window of opportunity)
- xz-utils: 約5週間
- Ultralytics: 12時間(1段階)、1時間(2段階)
- tj-actions: 3日
- chalk: 12時間未満
- Nx: 4時間
- rspack: 1時間
- num2words: 12時間未満
- Kong Ingress Controller: 約10日
- web3.js: 5時間
- このうち8件は 1週間未満の攻撃期間 で、ほとんどはクールダウンで防げる
クールダウンの概念と効果
- クールダウン(cooldown) は、新しい依存関係が公開された後、一定期間その使用を遅らせる方式
- この期間中にセキュリティベンダーが悪意の有無を検知できる
- 利点
- 実証的に効果があり、大規模攻撃の大半を防げる
- 実装が非常に簡単 で、ほとんどのツールで 無料で設定可能
- Dependabot の例
version: 2 - package-ecosystem: github-actions directory: / schedule: interval: weekly cooldown: default-days: 7 - セキュリティベンダーの前向きな行動を促す: 過剰なアラートや宣伝ではなく、迅速な検知に集中させる
結論と提言
- 10件中8件の攻撃は 1週間以下の期間 であり、7日間のクールダウン で大半を防げる
- 14日間のクールダウン を適用すれば、xz-utils を除くすべての事例を防御可能
- クールダウンは完璧な解決策ではないが、露出リスクを80〜90%減らせるシンプルな方法
- Dependabot、Renovate に加えて、パッケージマネージャー自体がクールダウンを標準サポート するよう改善が必要
- サプライチェーンセキュリティは技術的問題であるだけでなく、社会的な信頼構造 の問題でもあるが、クールダウンは現実的な緩和策として有用である
3件のコメント
実際、問題がなければ、あえてアップデートしないほうがよい気がします。
以前のバージョンと大きく変わらない新しいバージョンを、リスクを負ってまで必ず適用する必要があるのでしょうか。
Hacker Newsのコメント
すぐに更新しないと 深刻な脆弱性 にさらされると心配されがちだが、実際にはたいていそうではない
多くのソフトウェアは継続的デプロイではなく、顧客が自分で新バージョンをインストールする方式なので、更新は数週間から数か月単位で行われる
重要なのは 依存関係の監視 と公開された脆弱性の確認であり、製品が実際に影響を受けるかを評価したうえで、そのときだけ該当する依存関係を直ちに更新すればよい
新バージョンが出たら無条件で今日更新すべきだという認識が広がっている
実際の変更内容を確認せず、「後になるほど大変になるから今やろう」というやり方は非効率だ
バージョン番号の最前線に居続けることが、セキュリティ上 逆効果 になることすらある
私たちの会社では、スキャナーが重大な脆弱性を見つけると7日以内に更新しなければならない
期限を過ぎると規定違反として複雑な手続きが始まるため、たいていは何でも即座に更新してしまう
ブラウザのように外部入力が多いアプリは頻繁に更新すべきだが、天気アプリのように入力が限定される場合は比較的安全だ
むしろ定期的に更新しつつ、サプライチェーン攻撃への防御策 を併用するほうが効率的だ
Debian stableモデル のように、ディストリビューションが共通依存関係を管理し、数年ごとに全体アップグレードを行う方式は、ますます合理的に見える
一部のエコシステムは動きが速すぎたり、ディストリビューションごとのパッケージング体制が不十分だったりする
たとえば Node.js ライブラリを apt でインストールしてプロジェクトで使うのは、今でも難しい
根本的な変化もないまま速く動くエコシステムは健全ではない
JS はこの3年間ほとんど実質的な進歩がなく、3年前のプロジェクトを再ビルドしようとすると 書き直し級の苦痛 が伴う
Arch のようなディストリビューションでは、そもそも存在しないこともある
サプライチェーン攻撃の防止 のために、依存関係の更新に クールダウン期間 を設けるほうが、0-day を防ぐために最新バージョンを即座に使うより良いという考え方がある
これは、更新が新たな脆弱性を持ち込む確率と、既存の脆弱性を修正する確率を比べるということだ
SemVer の基準ではパッチバージョンは比較的安全なので、短いクールダウンを設けるというアプローチも可能だ
たとえば 2.3.4 から 2.4.0 が出たとき、急ぎの機能がないなら 2.4.1 が出るまで待ったほうがよい、という考え方だ
ほとんどの脆弱性は意図的な攻撃ではなく、一般的なバグ に由来する
依存関係の 数と複雑さ を制限するポリシーのほうが、より強力なアプローチだ
「何でもできるライブラリ」ではなく、小さくて目的が明確な依存関係だけを追加すべきだ
また、ライブラリ側も LTS バージョン を提供し、セキュリティパッチだけを含めるようにすれば管理しやすくなる
自分で再実装するのは無駄かもしれない。問題のある「everything library」の具体例が知りたい
同じ開発者を複数のライブラリで信頼しているなら、パッケージ数よりも 信頼関係 のほうが重要だ
複雑さは脆弱性と相関はあるが、直接の原因ではない
いまでは短期的な速度より、長期的な保守性を考慮した選択が可能になっている
C++ の世界では、こうした点が競争上の差別化要因になることもある
毎回最新バージョンへ更新しろという圧力は、ソフトウェアは常に良くなる という誤った信念から来ている
実際には、既知のバグを 新たな未知のバグ に置き換えているだけかもしれない
公開されている issue を監視し、必要なときだけパッチを当てるのが合理的だ
つまり、古いバージョンのほうがむしろ安全だった事例だ
おそらく、すでに十分安定したソフトウェアを使っているからでもあるのだろう
みんなが「少し待とう」と言い出すと、結局 共有地の悲劇 のように誰も先に検証しなくなる
待てば待つほど 技術的負債 が積み上がるので、段階的な更新と ゼロトラスト・モニタリング のような緩和策が必要だ
その間にセキュリティスキャナーがすでに脆弱性を検出する
攻撃者が不正なリリースを上げれば、すぐに気づかれることもある
クールダウンというアイデア自体は良いが、攻撃者がこれを悪用して 偽の緊急性 を作り出す危険がある
「緊急セキュリティパッチ」と称して早期インストールを促し、そのバージョン自体が実は悪意あるものかもしれない
このような 心理的圧力を利用した攻撃 に備える必要がある
つまり、攻撃者が世論を操作する方法への問いだ
クールダウンのもう一つの理由は、メンテナー自身が侵害を認識する時間 を与えることにある
攻撃者は、メンテナーが不在のタイミング(休暇、カンファレンス、祝日など)を狙う
多くのプロジェクトは1人か2人で管理されているため、こうした時間差が非常に重要だ
プロジェクトの性質と 攻撃面 によって異なる
単に「ベストプラクティス」に従えばよい時代は終わるべきだ
セキュリティは コンタクトスポーツ のようなもので、毎日細部を批判的に考えなければならない
単に時間が経てば安全になるとみなす クールダウンの限界 もある
誰もコードを見なければ、1週間経っても危険はそのままだ
その代わり、段階的デプロイ(gradual rollout) は有効かもしれない
各利用者が遅延期間(delay factor)を設定し、リスク許容度の高い側が先に問題に当たり、
その間、残りは保護されるという構造だ
危険な更新はキューから削除され、遅延させた利用者はそもそもそれを目にしなくなる
最近は、ただ車輪の再発明をする労力と、依存関係テトリスを管理する労力のどちらがまだ耐えられるのか、わからなくなることがあります。
しばらくの間うまくいかなくても自分のコードなら直せばいいだけですが、依存関係テトリスではどの車輪が突然狂ったのかを突き止めるのも難しく、デバッグもしづらいです。