依存関係の管理

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

ソフトウェア依存関係とは、ソフトウェア ライブラリやプラグインなど、アプリケーションの機能に必要なソフトウェアのことです。依存関係の解決は、コードのコンパイル、ビルド、実行、ダウンロード、インストールのときに発生します。

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

実装のベスト プラクティスの詳細は、アーティファクトの形式と使用するツールによって異なりますが、一般原則は適用されます。

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

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

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

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

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 コンソールのセキュリティ分析情報
Google Cloud は、Cloud Build、Cloud Run、GKE のアーティファクトのセキュリティ分析情報を提供します。これには、脆弱性、依存関係情報、ソフトウェア部品構成表(SBOM)、ビルドの来歴が含まれます。他の Google Cloud サービスにも、ソフトウェア開発ライフサイクル全体でセキュリティ対策を強化する機能が用意されています。詳細については、ソフトウェア サプライ チェーンのセキュリティの概要をご覧ください。
オープンソース ツール

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

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

  • Open Source Vulnerabilities データベース: 他のデータベースの脆弱性を 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 を使用して、各リポジトリへのアクセスを制限します。
    • リモート リポジトリを使用して、アップストリームのパブリック ソースの依存関係をキャッシュに保存し、脆弱性をスキャンします(非公開プレビュー)。
    • 仮想リポジトリを使用して、リモート リポジトリと限定公開リポジトリを 1 つのエンドポイントにグループ化します。アーティファクトのダウンロード時またはインストール時に検索順序を制御するには、各リポジトリに優先度を設定します(限定公開プレビュー)。
  • Artifact Registry は、Cloud Build、Cloud Run、Google Kubernetes Engine などの他の Google Cloud サービスで使用します。ソフトウェア開発ライフサイクル全体で自動脆弱性スキャンを使用し、ビルドの来歴を生成して、デプロイを制御し、セキュリティ体制に関する分析情報を表示します。

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

バージョンの固定

バージョンの固定は、アプリケーションの依存関係を特定のバージョンまたはバージョン範囲に制限することです。理想的には、依存関係の 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 に保存する前に対処できます。

次のステップ