依存関係の管理

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

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

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

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

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

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

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

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

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 を用いて、データを探索、分析できます。

  • オープンソースの脆弱性データベース: 他のデータベースの脆弱性を 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 サービスとともに使用します。ソフトウェア開発ライフサイクル全体で自動脆弱性スキャンを使用し、ビルドの来歴を作成して、デプロイを制御し、セキュリティ体制に関する分析情報を表示します。

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

バージョンの固定

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

依存関係のバージョンを固定すると、アプリのビルドを再現できるようになります。ただし、セキュリティの修正、バグの修正、改善など、依存関係のアップデートがビルドに含まれないことも意味します。

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

バージョンの固定は、直接依存関係にのみ適用されます。推移的依存関係には適用されません。たとえば、パッケージ 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 Analysis は、イメージが Artifact Registry にアップロードされるときにイメージをスキャンし、イメージを push してから最大 30 日間、新しい脆弱性を検出するためにイメージを継続的にモニタリングします。

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

次のステップ