はじめに
オープンソースソフトウェア(OSS)のおかげで、私たちは圧倒的な開発効率を享受しています。
しかしその一方で、常に脅威となるのが サプライチェーン攻撃 です。
最近では CrowdStrike を狙った npm パッケージ攻撃【参考】の流行っているように、OSSを利用する誰もがその影響を受ける可能性があります。
この記事では、GitHub 上で実践できるサプライチェーン防御策を 4つの観点 に分けて整理します。
1. 依存関係の固定と再現性
問題点
npm install
、pnpm install
、yarn install
を制約なしで実行すると、lockfile が存在していても依存関係が更新されてしまう場合があります。
私は「lockfile があれば常に再現性が担保される」と勘違いしていましたが、実際には package.json
と lockfile
の不整合や、解決ルールの差異によって lockfile が書き換わってしまうことがあります。
こうしたサイレントな更新は再現性を壊し、結果的にマルウェアを意図せず取り込むリスクを高めます。ほんの小さな依存解決の違いでも、環境ごとに大きく異なるリスクを生みかねません。
対策:
lockfile(package-lock.json
、pnpm-lock.yaml
など)を必ずコミット・維持し、依存関係を一貫して解決するようにします。
また、CI/CD で --frozen-lockfile
や --immutable
オプションを使って lockfile と依存関係が不一致の場合はビルドを失敗させると良いでしょう。これにより再現可能なビルドを保証し、意図しない変更がサプライチェーンに混入するのを防ぐことができます。
2. 依存関係の導入ガード
問題点
新しい依存関係を導入する際に、既知あるいは未知の悪意あるパッケージが紛れ込むリスクがあります。
package manager にもいくつかの防御機能がありますが、すべての脅威を防ぐことはできません。
対策
既知の脅威をブロックするために、GitHub Advisory Database、ossf/malicious-packages などの脅威インテリジェンスと利用しているパッケージを突合します。
※ 私は今回調べるまで知らなかったのですが、侵害されたパッケージの情報は GitHub Advisory Database に type: malware
として登録されるものの、dependabot alert には出てきてくれないようです。
dependency review action を利用すると GitHub Advisory Database と突合してくれるため、malware もブロックされるようになります。
未知のリスクに対しては静的解析ツールを利用し、タイポスクワッティングや不審なインストールスクリプトといった怪しい挙動を検知します。
DataDog/guarddog のようなツールを利用すれば、自動的に依存導入時のセキュリティチェック複数のルールに従って行ってくれます。
3. 更新管理
問題点
悪意あるパッケージはリリース直後が最も危険です。
今回の npm 関連の侵害事例についても、マルウェアはパッチバージョンを1つ上げてリリースするような動きをしていました。
Dependabot が即座にアップデートすると、コミュニティが脅威を発見する前にマルウェアを取り込んでしまう可能性があります。
対策
Dependabot の cooldown 設定 を利用して、アップデート適用前に短い遅延を設けましょう。
以下のように設定できます。
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 7
semver-major-days: 30
これにより、侵害されたパッケージの混入を完全には防ぎきれないものの、パッケージが侵害されたことに誰かが気付いて対処するまでの時間を稼ぐことができます。
ただし Security Patch については脆弱性の種類にもよりますが早急に取り込む必要があるなど運用のバランスは重要です。
4. 検証と監査性の確保
問題点
侵害されたパッケージの情報が公開された際に、自分たちのプロダクトにどう影響するのかを調査するのは大変です。特に npm の事例のように対象が膨大で、さらに運用しているリポジトリ数も多い場合は目も当てられません。
GitHub には依存関係グラフと SBOM(Software Bill of Materials)の生成機能がありますが、これは リポジトリ単位 でしか利用できません。organization や enterprise 単位で影響範囲を調べようとすると、リポジトリごとに API を叩く必要があり、しばしば RateLimit に引っ掛かります。
対策
Dependabot が PR を作成するなど、依存関係グラフに変更が入ったタイミングで GitHub Workflow を実行し、SBOM をエクスポート & 検索で簡単に引っ掛けられるようにしておくのは有効そうだなと思っています(試してはいないです)。
こうして検索可能な最新データを維持しておけば、新たな脅威が見つかった際にも影響範囲を迅速に把握して対処することができます。
GitHub Action: supplychain-guard
ここまで紹介した対策を実現するために、GitHub Action を作成してみました。
👉 Pirikara/supplychain-guard
セットアップ例
リポジトリに以下のような workflow ファイル(例: .github/workflows/supplychain-guard.yml
)を追加します:
name: Supply Chain Security
on:
pull_request:
paths:
- "package.json"
- "pnpm-lock.yaml"
permissions:
contents: read
pull-requests: write
jobs:
supply-chain-guard:
name: Supply Chain Guard
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
with:
fetch-depth: 0
- name: Supply Chain Guard
uses: Pirikara/supplychain-guard@main
with:
enable-ossf: "true"
enable-guarddog: "true"
guarddog-fail: "false"
warn-only: "false"
pr-comment: "true"
env:
GITHUB_TOKEN: ${{ github.token }}
依存関係が更新されたときにこのワークフローが実行されると、自動的に以下をチェックします:
lockfile の整合性 : そのロックファイルは正しいのか?
既知の悪意あるパッケージの混入 : GitHub Advisory DB / OSSF malicious packages に登録された悪性のパッケージと合致しないか?
不審な依存関係を検知する静的解析 : GuardDog による複数の静的解析に引っかからないか?
結果はいい感じにまとめて PR にコメントされます。
お試しで作ってみたものなのでフィードバックもらえると嬉しいです。
⚠️ dependency graphのdiffを取得するGitHub API を利用しているので、private repositoryで動かす場合は GitHub Advanced Security のライセンスが必要になります
おわりに
サプライチェーン攻撃は今後も引き続き大きなリスクになっていくでしょう。
しかし、OSS を使わないという選択肢は現実的ではなく、その恩恵を捨てることにもなります。
ここで整理した対策を行っても完全に防げるわけではないですが、80点を100点にするのはやはり厳しいので、気付いて止められるリスクにしっかり対処していくことが重要かなと考えています。