はじめに
供給チェーン攻撃シリーズでは、cooldownは「公開されたばかりの悪意あるバージョン」を防ぐ時間ベースのゲートであることを解説しました。しかし次の2つはcooldownの対象外です。
- すでに依存ツリーに入っているパッケージで新たに発見されたCVE — 公開から時間が経って正常と認められたバージョンで後から発見される脆弱性
- 自分たちのコード自体の脆弱性 — XSS、プロトタイプ汚染、インジェクションのようなown-codeの欠陥
この2つを防ぐのに最も効果的な2つのスキャナー — yarn npm auditとGitHub CodeQL — を解説します。
どんなリスクがあるか
リスク1. すでに入っている依存関係の新しいCVE
cooldownは新規公開の段階でのみ機能します。しかし、セキュリティの脆弱性は公開から数か月・数年後に発見されることの方が多いです。次の事例が代表的です。
- Log4Shell(2021、CVE-2021-44228): 8年間正常とされていたLog4jでcritical RCEが発見
- lodash prototype pollution(CVE-2019-10744): 長年安定して使用されていたlodashでprototype pollutionが発見
- xmldomなど多数のXMLパーサーの脆弱性: 長く使用されているライブラリで定期的に発見されるXXE/RCE
この種のリスクは「現在使用している依存関係に既知のCVEがあるか?」という問いに答えることで防げます。
リスク2. 自分たちのコード自体の脆弱性
供給チェーン防御が整っていても、自分たちが書いたコードに次のような欠陥があれば、それ自体が攻撃経路になります。
- DOM操作で
innerHTMLにユーザー入力をそのまま挿入 → XSS - オブジェクトのマージ時に
__proto__キーの検証漏れ → prototype pollution - 正規表現でのcatastrophic backtracking → ReDoS
- 動的な
import()パスにユーザー入力 → import injection
このような欠陥は依存関係のcooldownでは絶対に防げません。自分たちのコードを静的解析する必要があります。
yarn npm audit: 依存関係の既知CVEを検出
yarn npm auditはlockfileの依存関係をGitHub Advisory Databaseとマッチングして既知のCVEを見つけます。PRの段階でゲートとして追加すれば、新しいCVEがツリーに追加されるたびにPRが失敗します。
CIワークフローに追加
# .github/workflows/check_audit.yml
name: Dependency Audit
on:
pull_request:
schedule:
- cron: '0 0 * * *' # 毎日1回チェック
permissions:
contents: read
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<SHA> # v6
- uses: actions/setup-node@<SHA> # v6
- run: corepack enable
- run: yarn install --immutable
- run: yarn npm audit --severity high --recursive
オプションの意味:
| オプション | 説明 |
|---|---|
--severity high | high以上の深刻度のみ失敗として処理 |
--recursive | モノレポ全体のワークスペースを検査 |
効果
- 新しいCVEが登録されると次のPRまたは翌日のcronで即座に失敗
- 「このCVEが私たちに影響があるか?」という問いへの答えが毎日自動で出る
- false positiveが多ければ
--severity criticalで1段階さらに絞ることができる
CodeQL: 自分たちのコードの脆弱性を検出
GitHub CodeQLはpublicリポジトリでは無料で使用でき、privateリポジトリでもGitHub Code Scanning機能として利用可能です(GitHub Advanced Securityライセンスが必要)。
CodeQLはコードをデータベースに変換した後、セキュリティクエリ(事前定義された数百のルール)をSQLのように実行して脆弱性を見つけます。JavaScript/TypeScriptの場合、次のような欠陥を非常によく検出します。
- XSS(反射型、蓄積型、DOM型)
- Prototype pollution
- ReDoS(正規表現サービス拒否)
- 安全でないdeserialization
- インジェクション(SQL、command、path traversalなど)
CIワークフローに追加
# .github/workflows/codeql.yml
name: CodeQL
on:
push:
branches: [main]
pull_request:
schedule:
- cron: '0 0 * * 1' # 毎週月曜日に全スキャン
permissions:
contents: read
security-events: write
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@<SHA> # v6
- uses: github/codeql-action/init@<SHA>
with:
languages: javascript-typescript
- uses: github/codeql-action/analyze@<SHA>
分析結果はGitHub Securityタブの「Code scanning alerts」に表示され、PRにも自動でインラインコメントが追加されます。
実際の事例
GitHub Security LabはCommunity CodeQL Bounty Programを運営しており — コミュニティが作成したCodeQLクエリが — 実際のオープンソースプロジェクトで多数のCVEを発見した事例を公式ブログで紹介しています。JavaScript/TypeScriptの領域では、prototype pollution、ReDoS、XSS、安全でないdeserializationのようなパターンクラスに対して事前定義されたセキュリティクエリが日常的に新しい欠陥を検出しています。
詳細な発見事例のまとめはGitHub Security LabのCodeQL Wall of Fameで確認できます。
2つのスキャナーの役割分担
| ツール | 何を見るか | 何を防ぐか |
|---|---|---|
yarn npm audit | lockfileの依存関係 | 既知のCVE — データベースに登録された脆弱性 |
| CodeQL | 自分たちのソースコード | 未知のown-codeの欠陥 — XSS・インジェクション・プロトタイプ汚染 |
| (参考)cooldown | 新しく公開されるバージョン | 新規悪意ある公開 — 公開されたばかりのバックドア |
| (参考)Semgrep | 自分たちのソースコード(補完) | コードスタイル・セキュアパターンルール |
この3つはすべて異なる脅威を防ぎます。いずれか1つで他の2つを代替することはできません。
限界
yarn npm auditのfalse positive: 依存ツリーにあっても実際の使用パスでは呼び出されないコードである場合が多いです。それでもPRを失敗させるとノイズになります。--severityの閾値を調整するか、実際の影響分析は人間が判断する必要があります。- CodeQLの分析時間: 大きなモノレポではCodeQLの分析に数十分かかることがあります。PRごとに実行するのが重荷なら、pushと週次cronのみに適用する妥協案がよいでしょう。
- まだ発見されていないCVE: どちらも「すでに知られている」欠陥を見つけるツールです。0-dayは別の領域です。
まとめ
cooldownが新規公開をフィルタリングするとすれば、yarn npm auditとCodeQLはすでに入り込んでいるもの、または自分たちが作ったリスクを検出します。3つが合わさって初めて供給チェーンセキュリティが多層構造になります。
- まず導入するなら**
yarn npm auditをPRゲートに追加する**のが最も手軽です。設定1回で完了 - CodeQLは時間がかかりますが、own-codeの脆弱性発見に非常に効果的です。週次cronから始めて徐々にPRゲートに移していく段階的な導入を推奨します。
参考資料
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。