OWASP Dependency-CheckをCI/CDパイプラインに統合し、セキュリティを強化する

DevOps tutorial - IT technology blog
DevOps tutorial - IT technology blog

依存関係が負債になるとき:午前2時の目覚ましコール

午前2時。開発者と重大なアラートだけが活動しているような時間帯でした。PagerDutyからの通知が、私を深い眠りから引きずり出しました。数ヶ月間安定していた本番サービスが、突然、重大な脆弱性を報告し始めたのです。

原因は?目立たない推移的依存関係でした。それは定期的な更新中にひっそりと取り込まれ、最近NVD(National Vulnerability Database)に追加されたものでした。その場は混乱を極めました。緊急パッチの適用、デプロイのロールバック、そして大量のカフェインを必要としました。

あの夜は、重要な教訓を私に深く刻み込みました。単に構築してデプロイするだけでは不十分です。脆弱性が本番環境に到達する前に、積極的に脆弱性を探し出す必要があります。私の実体験から言えば、このスキルを習得すること、つまり依存関係チェーンを理解し、保護することは極めて重要です。

手動 vs. 自動依存関係スキャン:2つのアプローチ

実践的な詳細に入る前に、チームがどのように依存関係のセキュリティを管理しているか、あるいは、管理して*いない*ことが多いかについて説明しましょう。一般的に、主要なアプローチは2つあります。

手動の迷路

多くのチームは、当初手動アプローチを採用しています。これは通常、誰かが定期的にプロジェクトの依存関係を既知の脆弱性データベースと照合することを意味します。さらに悪いことに、一部のチームは、重大な脆弱性が公に開示され、自社のスタックに直接影響を与えたときにのみ反応します。これは多くの場合、次のようになります。

  • package.jsonrequirements.txtpom.xmlなどの設定ファイルを手動でレビューする。
  • セキュリティメーリングリストやNVDフィードを購読する。
  • 時間があるときにのみ、さまざまなツールを使用してアドホックなスキャンを実行する。

ここでの根本的な問題は、規模と人為的なエラーです。プロジェクトは成長し、依存関係は急速に増加するため、すべての更新と脆弱性を追跡することは、数人の個人にとって圧倒的なタスクになります。この方法は遅く、反応的で、信頼性に欠けることで悪名高いです。

自動化された武器

自動化された依存関係スキャンは、セキュリティチェックを開発ワークフローに直接組み込みます。OWASP Dependency-Checkのようなツールは、多くの場合CI/CDパイプラインの一部として、スキャンを自動的に実行します。これらは、常に人間が介入することなく、脆弱なコンポーネントを特定します。このプロアクティブな戦略により、セキュリティの「シフトレフト」が可能になります。開発ライフサイクルのずっと早い段階で問題を発見し修正できるため、解決にかかる費用が大幅に安くなり、中断も少なくなります。

自動依存関係チェックの長所と短所

自動スキャンは明確な利点を提供しますが、すべてのセキュリティ問題を魔法のように解決するわけではありません。特にOWASP Dependency-Checkのようなツールを使う場合、その長所と短所を理解することは、現実的な期待値を設定するのに役立ちます。

利点:なぜ自動化するのか?

  • 早期検出(シフトレフト):開発中またはテスト中に脆弱性を発見し、本番環境に到達するずっと前に対応できます。これにより、リリース後に問題を修正する場合と比較して、修復コストを最大100倍削減できます。
  • 一貫性と信頼性:自動ツールは疲れたり、間違いを犯したりしません。毎回同じチェックを適用するため、見落としのリスクが減少します。
  • 包括的なカバレッジ:OWASP Dependency-Checkは、既知の脆弱性の広範なデータベースを維持しており、多数のプログラミング言語とエコシステム(例:Java、.NET、Node.js、Python、Ruby、PHP)をカバーしています。NVD、GitHub Advisoriesなどのソースからデータを集約しています。
  • コンプライアンス:規制遵守(例:GDPR、HIPAA、ISO 27001)にとって非常に重要であり、ソフトウェアサプライチェーンのリスク管理におけるデューデリジェンスを示すのに役立ちます。
  • オープンソースかつ無料:コミュニティ主導のプロジェクトとして、OWASP Dependency-Checkは無料で利用でき、コミュニティの貢献によって常に改善されています。

短所:考慮すべき課題

  • 誤検知:完璧なセキュリティツールはありません。Dependency-Checkは、特定のコンテキストでは実際には脆弱ではないコンポーネント(例:脆弱な関数が呼び出されていない場合)を誤って指摘することがあります。これらはレビューと、場合によっては抑制が必要です。
  • 設定のオーバーヘッド:特定のプロジェクトとCI/CD環境に対する初期設定と微調整には、かなりの時間がかかる場合があります。
  • パフォーマンスへの影響:スキャンはCI/CDパイプラインに時間を追加する可能性があります。数百もの依存関係を持つ非常に大規模なプロジェクトの場合、ビルド時間が数分延長されることがあります。
  • 定期的な更新が必要:Dependency-Checkが使用する脆弱性データベースは常に更新されています。効果を維持し、最新の結果を提供するためには、スキャナーがこれらの更新を定期的に取得する必要があります。
  • 既知の脆弱性に限定:公開され、そのデータベース(NVDやRetire.jsなど)に追加された脆弱性のみを検出します。ゼロデイエクスプロイトやアプリケーション固有のロジックの欠陥は見つけられません。

推奨されるセットアップ:OWASP Dependency-Checkを始める

ほとんどのCI/CD環境では、OWASP Dependency-Checkをスタンドアロンのコマンドラインツールとして、またはDockerイメージ経由で実行すると、最高の柔軟性と制御が得られます。開始するために必要なものは次のとおりです。

前提条件

  • Java Runtime Environment (JRE):Dependency-CheckはJavaアプリケーションです。CI/CDランナーまたはDockerコンテナ内にJava 8以降がインストールされていることを確認してください。
  • Git:CI/CDパイプラインが機能するためには、プロジェクトコードがGit経由でアクセス可能である必要があります。
  • CI/CDシステム:シェルコマンドを実行できるシステムであればどれでも動作します。一般的な例としては、Jenkins、GitLab CI、GitHub Actions、Azure DevOps、CircleCIなどがあります。

インストールオプション

オプション1:スタンドアロンCLI(ベアメタルランナーに最適)

公式GitHubリリースページから最新リリースを直接ダウンロードしてください。LinuxベースのCI/CDランナーでは、これらのコマンドを使用できます。

# 最新のCLIをダウンロード(必要に応じてバージョンを調整)
WGET_URL=$(curl -s https://api.github.com/repos/jeremylong/DependencyCheck/releases/latest | grep "browser_download_url.*zip" | cut -d '"' -f 4)
wget -q $WGET_URL
unzip dependency-check-*.zip
mv dependency-check-* dependency-check
chmod +x dependency-check/bin/dependency-check.sh

オプション2:Dockerイメージ(コンテナ化されたCI/CDに推奨)

これは多くの場合、最もクリーンな方法です。ツールとその依存関係を完全に分離します。公式のDockerイメージは活発にメンテナンスされており、すぐに利用できます。

docker pull owasp/dependency-check

実装ガイド:CI/CDパイプラインへの統合

Dependency-Checkを一般的なCI/CDワークフローに統合する手順を説明します。ほとんどの最新のCI/CDシステムに適用できる例を、コマンドラインの使用に焦点を当てて提供します。

ステップ1:基本的なスキャンを実行する

まず、基本的なコマンドを把握しましょう。プロジェクトのルートに移動(またはDependency-Checkがアクセスできることを確認)し、スキャンを開始します。--scan引数はプロジェクトディレクトリを指し、--projectはスキャン結果に名前を割り当てます。

# スタンドアロンCLIを使用する場合
./dependency-check/bin/dependency-check.sh \
  --scan . \
  --project "MyAwesomeWebApp" \
  --format HTML \
  --out "./dependency-check-report.html"

# Dockerを使用する場合
docker run --rm \
  -v $(pwd):/src \
  owasp/dependency-check \
  --scan /src \
  --project "MyAwesomeWebApp" \
  --format HTML \
  --out /src/dependency-check-report.html

このコマンドは、現在のディレクトリ(Dockerでは.または/src)をスキャンし、プロジェクトに「MyAwesomeWebApp」という名前を付け、dependency-check-report.htmlという名前のHTMLレポートを生成します。Dependency-Checkは、HTML、XML、JSON、CSV、JUNITなど、さまざまな出力形式をサポートしています。

ステップ2:CI/CDシステムとの統合(例:GitHub Actions)

真の利点は、このプロセスを自動化することから生まれます。ここでは、GitHub ActionsのワークフローにDependency-Checkを組み込む方法を紹介します。重要な決定点の1つは、いつビルドを失敗させるかを決定することです。

name: CI/CDパイプライン

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-and-scan:
    runs-on: ubuntu-latest
    steps:
    - name: コードをチェックアウト
      uses: actions/checkout@v3

    - name: OWASP Dependency-Checkを実行
      id: dependency-check
      uses: jeremylong/dependency-check-action@v9
      with:
        project: 'MyGitHubProject'
        path: '.' # 現在のディレクトリをスキャン
        format: 'HTML,JSON'
        failOnCVSS: 7 # いずれかの脆弱性のCVSSスコアが7以上の場合に失敗
        # オプション:アーティファクトの特定のレポートパスを設定
        # reportPath: 'dependency-check-report'

    - name: Dependency-Checkレポートをアップロード (HTML)
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: dependency-check-report-html
        path: dependency-check-report.html

    - name: Dependency-Checkレポートをアップロード (JSON)
      uses: actions/upload-artifact@v3
      if: always()
      with:
        name: dependency-check-report-json
        path: dependency-check-report.json

このGitHub Actionsの例の主なポイント:

  • 簡潔にするために、コミュニティがメンテナンスしているjeremylong/dependency-check-action@v9を使用しています。これはCLIツールのラッパーとして機能します。
  • failOnCVSS: 7:この設定は非常に重要です。これは、特定された脆弱性のいずれかのCVSS(共通脆弱性評価システム)スコアが7.0以上の場合にビルドを失敗させるようDependency-Checkに指示します。これは通常、高または重大な深刻度の脆弱性に対応します。このしきい値は、チーム固有のリスク許容度に基づいて調整してください。
  • レポートはアーティファクトとしてアップロードされるため、パイプラインの実行完了後に簡単にレビューできます。

他のCI/CDシステムについても、基本的なロジックは一貫しています。ビルドステップ内でdependency-check.shコマンド(またはそのDocker相当物)を実行します。プロジェクトをスキャンするための正しい引数を渡し、適切な失敗しきい値を設定していることを確認してください。

ステップ3:レポートのレビューとトリアージ

スキャン後、選択した形式でレポートを受け取ります。HTMLレポートは通常、最も読みやすく、特定された脆弱性、影響を受ける依存関係、および提案された解決策の明確な概要を提供します。

レビュー時には、次の点に注意してください。

  • コンポーネント:脆弱性を含む特定のライブラリまたはフレームワーク。
  • 脆弱性:CVE(Common Vulnerabilities and Exposures)に関する詳細情報。
  • CVSSスコア:脆弱性の深刻度を示す数値評価。
  • 解決策:多くの場合、脆弱性のない更新された依存関係のバージョンを指します。

すべてを盲目的に修正しないでください。トリアージは重要なステップです。

  • 優先順位付け:最も差し迫ったリスクをもたらす、CVSSスコアが高いまたは重大な脆弱性にまず焦点を当てます。
  • 文脈化:依存関係の脆弱な部分が、アプリケーションのコードで実際に使用されていますか?特定の利用パターンによっては、脆弱性が悪用できない場合があります。
  • アップグレード:最も一般的で最良の解決策は、脆弱性がパッチ適用されたバージョンに依存関係をアップグレードすることです。
  • 抑制:検出結果が誤検知である、または実際に適用できないと自信を持って判断した場合、それを抑制できます。

ステップ4:抑制ファイルによる誤検知の管理

誤検知はセキュリティツールにおいて避けられない現実です。ビルドが繰り返し失敗するのを防ぐため、Dependency-Checkは抑制ファイル(XML形式)の使用をサポートしています。

<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">
    <!-- 例:特定の依存関係に対する特定のCVEを抑制する -->
    <suppress>
        <notes>誤検知:このCVEはXが有効な場合にのみ悪用可能ですが、それは当社の設定ではありません。</notes>
        <cve>CVE-2023-12345</cve>
        <filePath regex="true">.*my-vulnerable-library-1.0.jar</filePath>
    </suppress>

    <!-- 例:特定の依存関係に対するすべての検出結果を抑制する(注意して使用) -->
    <suppress>
        <notes>このライブラリは内部で個別にスキャンされます。外部の脆弱性は適用されません。</notes>
        <fileName regex="true">.*my-internal-component.jar</fileName>
    </suppress>
</suppressions>

この内容を、例えばdependency-check-suppressions.xmlとしてプロジェクトのルートディレクトリに保存します。その後、スキャンコマンドに--suppression引数を追加します。

# スタンドアロンCLIを使用する場合
./dependency-check/bin/dependency-check.sh \
  --scan . \
  --project "MyAwesomeWebApp" \
  --format HTML \
  --out "./dependency-check-report.html" \
  --suppression "./dependency-check-suppressions.xml"

# Dockerを使用する場合
docker run --rm \
  -v $(pwd):/src \
  owasp/dependency-check \
  --scan /src \
  --project "MyAwesomeWebApp" \
  --format HTML \
  --out /src/dependency-check-report.html \
  --suppression /src/dependency-check-suppressions.xml

各抑制が行われた理由を必ず文書化してください。抑制はまれな例外であり、一般的な慣行ではないことを忘れないでください。また、定期的なレビューが必要です。

結論:ソフトウェアサプライチェーンのセキュリティ保護

OWASP Dependency-CheckをCI/CDパイプラインに統合することは、より安全なソフトウェアを構築するための基本的なステップです。それは、依存関係管理を、反応的で深夜の緊急対応から、プロアクティブで自動化された、開発プロセスに不可欠な一部へと変革します。

初期設定と検出結果のトリアージへの継続的な注意は必要ですが、それによって得られる安心感、そして節約される睡眠時間は計り知れません。あなた自身の午前2時の目覚ましコールを待つことなく、今日から依存関係の脆弱性スキャンをソフトウェア開発ライフサイクルのコアコンポーネントにしましょう。

Share: