依存関係の管理

このドキュメントでは、アプリケーションの依存関係と、脆弱性を管理するためのベスト プラクティス(脆弱性モニタリング、アーティファクトの確認、依存関係のフットプリントの低減、再現可能なビルドのサポートなど)について説明します。

ソフトウェアの依存関係は、ソフトウェア ライブラリやプラグインなど、アプリケーションが機能するために必要なソフトウェアの一部です。依存関係の解決は、コードのコンパイル、ビルド、実行、ダウンロード、インストール時に行われます。

依存関係には、作成したコンポーネント、独自のサードパーティ ソフトウェア、オープンソース ソフトウェアの両方が含まれます。依存関係を管理するアプローチは、アプリケーションのセキュリティと信頼性に影響を与える可能性があります。

ベスト プラクティスを実装する具体的な方法は、アーティファクトの形式と使用するツールによって異なりますが、一般的な原則は引き続き適用されます。

直接的および推移的な依存関係

アプリケーションには、直接的な依存関係と推移的な依存関係の両方を含めることができます。

直接的な依存関係
アプリケーションが直接参照するソフトウェア コンポーネント。
推移的依存関係
アプリケーションの直接依存関係で機能的に必要なソフトウェア コンポーネント。 各依存関係には、独自の直接的および間接的な依存関係があり、すべてがアプリケーションに影響する、推移的な依存関係の再帰ツリーが作成されます。

プログラミング言語ごとに、依存関係とその関係性をさまざまなレベルで可視化できます。また、一部の言語では、パッケージ マネージャーを使用して、パッケージのインストールまたはデプロイ時に依存関係ツリーを解決します。

Node.js エコシステムでは、npm と yarn のパッケージ マネージャーがロック ファイルを使用して、モジュールをビルドする依存関係のバージョンと、パッケージ マネージャーがモジュールの特定のインストールのためにダウンロードする依存関係のバージョンを特定します。Java などの他の言語エコシステムでは、依存関係のイントロスペクションのサポートはさらに制限されています。また、ビルドシステムでは、特定の依存関係マネージャーを使用して依存関係を体系的に管理する必要があります。

たとえば、npm モジュール glob のバージョン 8.0.2 を考えてみましょう。npm モジュールの依存関係はファイル package.json で直接宣言します。glob の package.json ファイルdependencies セクションには、公開されているパッケージの直接の依存関係が一覧表示されます。 devDepdencies セクションには、glob のメンテナとコントリビューターによるローカルでの開発とテストの依存関係が一覧表示されます。

  • npm ウェブサイトでは、glob ページに直接的な依存関係と開発の依存関係が一覧表示されますが、これらのモジュールに独自の依存関係があるかどうかは示されません。

  • glob に関する追加の依存関係情報は、Open Source Insights のサイトで確認できます。glob の依存関係リストには、直接的な依存関係と間接的な(推移的な)依存関係の両方が含まれています。

    推移的依存関係は、依存関係ツリー内の複数のレイヤにできます。 例:

    1. glob 8.0.2 は minimatch 5.0.1 に直接依存しています。
    2. minimatch 5.0.1 は brace-expression 2.0.1 に直接依存しています。
    3. brace-expression 2.0.1 は balanced-match 1.0.2 に直接依存しています。

間接的な依存関係を可視化しなければ、コードで直接参照されないコンポーネントに起因する脆弱性やその他の問題を特定して対処することは非常に困難です。

glob パッケージをインストールすると、npm は依存関係ツリー全体を解決し、ダウンロードした特定のバージョンのリストを package.lock.json ファイルに保存します。これにより、すべての依存関係の記録が用意されます。同じ環境でその後のインストールを行うと、同じバージョンが取得されます。

依存関係の分析情報に関するツール

次のツールを使用して、オープンソースの依存関係を理解し、プロジェクトのセキュリティ体制を評価できます。 これらのツールは、パッケージ形式全体の情報を提供します。

ソフトウェア デリバリー シールド
Google Cloud 上のフルマネージド ソフトウェア サプライ チェーンのセキュリティ ソリューションです。脆弱性、依存関係情報、ソフトウェア部品構成表(SBOM)、ビルドの来歴など、Cloud Build、Cloud Run、GKE のアーティファクトに対するセキュリティ分析情報を表示できます。Software Delivery Shield は、ソフトウェア開発ライフサイクル全体でセキュリティ対策を改善する他のサービスや機能も提供します。
オープンソース ツール

次のようなオープンソースのツールを使用できます。

  • Open Source Insights: オープンソース ソフトウェアについて、直接的および間接的な依存関係、既知の脆弱性、ライセンス情報に関する情報を提供するウェブサイト。Open Source Insights プロジェクトでは、このデータを Google Cloud データセットとしても利用できます。 BigQuery を使用して、データを探索および分析できます。

  • オープンソースの脆弱性データベース: 他のデータベースの脆弱性を 1 か所に集約する、検索可能な脆弱性データベース。

  • Scorecards: GitHub プロジェクトでリスクのあるソフトウェア サプライ チェーンの運用を特定するために使用できる自動ツール。リポジトリに対してチェックを実行し、チェックに 0 ~ 10 のスコアを付けます。その後、そのスコアを使用して、プロジェクトのセキュリティ体制を評価できます。

  • Allstar 構成済みのポリシーへの準拠に関して、GitHub 組織のリポジトリを継続的にモニタリングする GitHub アプリ。たとえば、管理者または push アクセス権を持つ組織外の共同編集者をチェックするポリシーを GitHub 組織に適用できます。

依存関係を含める方法

アプリケーションに依存関係を含めるには、いくつかの一般的な方法があります。

パブリック ソースから直接インストールする
Docker Hub、npm、PyPI、Maven Central などのパブリック リポジトリからオープンソースの依存関係を直接インストールします。このアプローチは、外部依存関係を維持する必要がないため便利です。ただし、このような外部依存関係は制御できないため、ソフトウェア サプライ チェーンでオープンソース サプライ チェーン攻撃が発生しやすくなります。
依存関係のコピーをソース リポジトリに保存する
この手法は、ベンダリングとも呼ばれます。ビルド中にパブリック リポジトリから外部依存関係をインストールする代わりに、依存関係をダウンロードしてプロジェクトのソースツリーにコピーします。使用するベンダー依存の依存関係はより詳細に制御できますが、いくつかの欠点があります。
  • ベンダリングの依存関係によってソース リポジトリのサイズが拡大し、チャーンが増加します。
  • 同じ依存関係を個別のアプリケーションにベンダリングする必要があります。ソース リポジトリまたはビルドプロセスが再利用可能なソース モジュールをサポートしていない場合は、依存関係の複数のコピーを保持する必要があります。
  • ベンダー化された依存関係のアップグレードは難しい場合があります。
非公開レジストリに依存関係を保存する
Artifact Registry などの非公開レジストリは、パブリック リポジトリからインストールできるだけでなく、依存関係の制御も可能です。Artifact Registry では次のことが可能です。
  • すべてのアプリケーションのビルド アーティファクトと依存関係を一元管理します。
  • パブリック リポジトリと同じ方法で、Artifact Registry のプライベート リポジトリとやり取りするように Docker と言語パッケージ クライアントを構成します。
  • プライベート リポジトリの依存関係を細かく制御できます。
  • Identity and Access Management を使用して各リポジトリへのアクセスを制限します。
  • リモート リポジトリを使用して、アップストリームのパブリック ソースの依存関係をキャッシュに保存し、脆弱性をスキャンします(非公開プレビュー)。
  • 仮想リポジトリを使用して、単一のエンドポイントの背後にあるリモート リポジトリとプライベート リポジトリをグループ化します。アーティファクトをダウンロードまたはインストールするときに、各リポジトリに優先度を設定して検索順序を制御します(限定公開プレビュー)。
  • Cloud Build、Cloud Run、Google Kubernetes Engine など、Software Delivery Shield の他の Google Cloud サービスと Artifact Registry を簡単に使用できます。ソフトウェア開発ライフサイクル全体で自動脆弱性スキャンを使用し、ビルドの来歴を作成して、デプロイを制御し、セキュリティ体制に関する分析情報を表示します。

可能であれば、依存関係にはプライベート レジストリを使用してください。プライベート レジストリを使用できない場合は、ソフトウェア サプライ チェーン内のコンテンツを制御できるように、依存関係をベンダリングすることを検討してください。

バージョンの固定

バージョンの固定は、アプリケーションの依存関係を特定のバージョンまたはバージョン範囲に制限することです。依存関係の 1 つのバージョンを固定するのが理想的です。

依存関係のバージョンを固定すると、アプリケーション ビルドを再現可能にできます。ただし、これはセキュリティ修正、バグ修正、改善などの依存関係の更新がビルドに含まれていないことを意味します。

この問題は、新しいリリースのソースの依存関係をモニタリングする自動依存関係管理ツールを使用することで軽減できます。これらのツールは、必要に応じて依存関係ファイルをアップグレードするために要件ファイルを更新します。多くの場合、変更履歴や追加情報が含まれます。

バージョンの固定は、直接依存関係にのみ適用されます。推移的依存関係には適用されません。たとえば、パッケージ my-library のバージョンを固定すると、固定によって my-library のバージョンは制限されますが、my-library が依存関係にあるソフトウェアのバージョンは制限されません。ロック ファイルを使用すると、一部の言語でパッケージの依存関係ツリーを制限できます。

署名とハッシュの検証

依存関係として使用するアーティファクトの信頼性を検証するために使用できるメソッドがいくつかあります。

ハッシュ検証

ハッシュは、一意の識別子として生成されるファイルの値です。アーティファクトのハッシュとアーティファクトのプロバイダによって計算されたハッシュ値を比較して、ファイルの整合性を確認できます。ハッシュ検証は、中間者攻撃やアーティファクト リポジトリの不正使用による依存関係の置換、改ざん、破損を識別するのに役立ちます。

ハッシュ検証を使用するには、アーティファクト リポジトリから受信したハッシュが不正使用されていないという信頼が必要です。

署名の検証

署名の検証を実施すると、検証プロセスのセキュリティが高まります。アーティファクト リポジトリ、ソフトウェアの管理者、またはその両方がアーティファクトに署名できます。

sigstore などのサービスにより、管理者がソフトウェア アーティファクトに署名し、コンシューマがこれらの署名を検証できます。

Binary Authorization は、Google Cloud ランタイム環境にデプロイされたコンテナ イメージが、さまざまな基準で証明書で署名されていることを検証できます。

ロック ファイルとコンパイルされた依存関係

ロック ファイルは、完全に解決された要件ファイルであり、アプリケーションに対してインストールする必要があるすべての依存関係のバージョンを正確に指定します。ロック ファイルは通常、インストール ツールによって自動的に生成され、バージョンの固定および署名またはハッシュの検証を、アプリケーションの完全依存関係ツリーと結合します。

インストール ツールは、最上位の依存関係のすべてのダウンストリームの推移的な依存関係を完全に解決してから依存関係ツリーを作成し、ロック ファイルに依存関係ツリーを含めます。その結果、これらの依存関係のみをインストールできるため、ビルドの再現性と一貫性が高まります。

プライベートの依存関係とパブリックの依存関係の混在

最先端のクラウドネイティブ アプリケーションは多くの場合、オープンソースのサードパーティ コードとクローズド ソースの内部ライブラリの両方に依存します。Artifact Registry を使用すると、複数のアプリケーションでビジネス ロジックを共有し、同じツールを使用して外部と内部の両方のライブラリをインストールできます。

ただし、プライベートとパブリックの依存関係を混在させると、ソフトウェア サプライ チェーンが依存関係の混同攻撃に対する脆弱性が高くなります。内部プロジェクトと同じ名前のプロジェクトをオープンソース リポジトリに公開することで、攻撃者が誤った構成のインストーラを利用して、内部依存関係ではなく悪意のあるコードをインストールする可能性があります。

依存関係の混同攻撃を防ぐための手順は、次のようにいくつもあります。

  • 依存関係の署名とハッシュを検証するため、これらをロック ファイルに組み込む。
  • サードパーティの依存関係のインストールと内部依存関係のインストールを 2 つのステップに分ける。
  • プライベート リポジトリに必要なサードパーティの依存関係を、手動またはプロキシを介した pull で明示的にミラーリングする。Artifact Registry のリモート リポジトリは、アップストリーム パブリック リポジトリの pull スルー プロキシです。
  • 仮想リポジトリを使用して、リモート リポジトリと標準 Artifact Registry リポジトリを 1 つのエンドポイントに統合します。非公開アーティファクトのバージョンが常に同じ名前の公開アーティファクトよりも優先されるように、アップストリーム リポジトリの優先度を構成できます。
  • 公開パッケージとベースイメージには信頼できるソースを使用します。

未使用の依存関係の除外

ニーズの変化とアプリケーションの進化に伴い、一部の依存関係の変更や停止が発生する可能性があります。未使用の依存関係をアプリケーションにインストールし続けると、依存関係のフットプリントが増加し、依存関係の脆弱性によって侵害されるリスクが高くなります。

アプリケーションをローカルで動作させたら、開発プロセス中にインストールしたすべての依存関係をアプリケーションの要件ファイルにコピーするのが一般的な方法です。次に、これらのすべての依存関係を持つアプリケーションをデプロイします。このアプローチは、デプロイされたアプリケーションが機能することを保証するのに役立ちますが、本番環境に不要な依存関係を引き起こす可能性もあります。

アプリケーションに新しい依存関係を追加する際は注意が必要です。各データベースは、完全に制御できないコードをさらに導入する可能性があります。通常の lint チェックとテスト パイプラインの一部として、要件ファイルを監査するツールを統合して、依存関係を実際に使用またはインポートしているかどうかを判断します。

一部の言語には、依存関係の管理に役立つツールがあります。たとえば、Maven 依存関係プラグインを使用して Java の依存関係を分析し、管理できます。

脆弱性スキャン

依存関係の脆弱性に迅速に対応することで、ソフトウェア サプライ チェーンを保護できます。

脆弱性スキャンを使用すると、依存関係がアプリケーションに脆弱性をもたらしているかどうか、一貫して自動的に評価を行えます。脆弱性スキャンツールはロック ファイルを使用して、依存するアーティファクトを正確に見極め、新たな脆弱性が表面化したときにデベロッパーに通知します。アップグレード方法を提案することもあります。

たとえば、Artifact Analysis はコンテナ イメージの OS パッケージの脆弱性を特定します。Artifact Registry にアップロードされたイメージをスキャンし、イメージを push してから最大 30 日間、新しい脆弱性を検出するためにイメージを継続的にモニタリングできます。

オンデマンド スキャンを使用して、OSGoJava のコンテナ イメージをローカルにスキャンすることもできます。これにより、脆弱性を早期に識別できるため、Artifact Registry に保存する前に対処できます。

次のステップ