セキュリティスキャナのインテグレーション

セキュリティスキャナーをGitLabにインテグレーションすることは、エンドユーザーがGitLabプロジェクトをスキャンするためにCI設定ファイルに追加できるCIジョブの定義を提供することです。 このCIジョブは、GitLabが指定したフォーマットで結果を出力する必要があります。 これらの結果は、パイプラインビュー、マージリクエストウィジェット、セキュリティダッシュボードのようなGitLabの様々な場所で自動的に表示されます。

スキャンジョブは通常、自己完結型の環境にスキャナとそのすべての依存関係を含むDockerイメージに基づいています。

このページは、セキュリティスキャナを実装する CI ジョブを書くための要件とガイドライン、および、Docker イメージの要件とガイドラインを文書化したものです。

ジョブ定義

このセクションでは、セキュリティスキャナのジョブ定義ファイルに追加するいくつかの重要なフィールドについて説明します。 これらのフィールドおよびその他の利用可能なフィールドに関する完全なドキュメントは、CIのドキュメントで見ることができます。

名称

一貫性を保つため、スキャニングジョブにはスキャナの名前を小文字で付ける必要があります。 ジョブ名の末尾にはスキャニングの種類を付けます:_dependency_scanning,_container_scanning,_dast,_sast. 例えば、「MySec」スキャナに基づく依存関係スキャニングジョブはmysec_dependency_scanningという名前になります。

画像

image キーワードは、セキュリティスキャナを含むDocker イメージを指定するために使用します。

Script

script キーワードは、スキャナを実行するコマンドを指定するために使用されます。script エントリを空にすることはできないため、スキャンを実行するコマンドを設定する必要があります。 Docker イメージの事前定義されたENTRYPOINTCMD に依存して、コマンドを渡さずに自動的にスキャンを実行することはできません。

before_script例えば、before_script 、SAST や依存関係スキャンを実行する前に、特定のプロジェクトが必要とするシステムライブラリをインストールするのが一般的です。

同様に、after_scriptはジョブ定義で使用すべきではありません。なぜなら、ユーザーによって上書きされる可能性があるからです。

ステージ

一貫性を保つため、スキャンジョブは可能な限りtest ステージに属するようにします。test がデフォルト値であるため、stage キーワードは省略できます。

フェイルセーフ

GitLab Securityのパラダイムに合わせるために、スキャンジョブは失敗してもパイプラインをブロックしないように、allow_failure パラメータはtrueに設定します。

アーティファクト

スキャンジョブは、artifacts:reports キーワードを使用して、実行するスキャンのタイプに対応するレポートを宣言する必要があります。有効なレポートは、dependency_scanningcontainer_scanningdastsastです。

例えば、gl-sast-report.jsonという名前のファイルを生成し、SAST レポートとしてアップロードする SAST ジョブの定義を以下に示します:

mysec_sast:
  image: registry.gitlab.com/secure/mysec
  artifacts:
    reports:
      sast: gl-sast-report.json

gl-sast-report.json はファイルパスの例ですが、他のファイル名も使用できることに注意してください。 詳細は出力ファイルのセクションを参照してください。 ジョブ定義のreports:sast キーで宣言されているため、ファイル名ではなく、SAST レポートとして処理されます。

ポリシー

AutoDevOpsのような特定のGitLabワークフローでは、指定されたスキャンを無効にすべきことを示す変数が定義されています。DEPENDENCY_SCANNING_DISABLED,CONTAINER_SCANNING_DISABLED,SAST_DISABLED,DAST_DISABLEDのような変数を探すことで、これをチェックすることができます。スキャナの種類に基づいて適切であれば、カスタムスキャナの実行を無効にすべきです。

GitLab はCI_PROJECT_REPOSITORY_LANGUAGES 変数も定義しており、リポジトリ内の言語のリストを提供します。 この値によって、スキャナは異なる動作をしたりしなかったりします。 言語検出は今のところlinguist Ruby gem に依存しています。GitLab CI/CD の定義済み変数を参照してください。

ポリシーチェックの例

この例では、プロジェクト・リポジトリに Java ソース・コードが含まれていて、dependency_scanning 機能が有効になっていない限り、カスタム依存関係スキャン・ジョブ(mysec_dependency_scanning)をスキップする方法を示します:

mysec_dependency_scanning:
  rules:
    - if: $DEPENDENCY_SCANNING_DISABLED
      when: never
    - if: $GITLAB_FEATURES =~ /\bdependency_scanning\b/
      exists:
        - '**/*.java'

追加のジョブポリシーは、ユーザーが必要に応じて設定する必要があります。 例えば、事前に定義されたポリシーは、特定のブランチや特定のファイルセットが変更されたときにスキャンジョブをトリガしないようにする必要があります。

docker イメージ

Dockerイメージは、スキャナと、それに依存するすべてのライブラリやツールを組み合わせた自己完結型の環境です。 スキャナをDockerイメージにパッケージ化することで、スキャナが実行される個々のマシンに関係なく、その依存関係と設定が常に存在するようになります。

画像サイズ

CIインフラストラクチャによっては、CIはジョブが実行されるたびにDockerイメージをフェッチしなければならないかもしれません。 スキャンジョブを高速に実行し、帯域幅の浪費を避けるためには、Dockerイメージはできるだけ小さくする必要があります。 50MB以下を目指すべきです。 それが不可能な場合は、CD-ROMのサイズである1.46GB以下に抑えるようにしてください。

スキャナが完全に機能する Linux 環境を必要とする場合は、Debian“slim” ディストリビューションまたはAlpine Linuxを使用することをお勧めします。 可能であれば、FROM scratch 命令を使用してゼロからイメージをビルドし、スキャナが必要とするすべてのライブラリをコンパイルすることをお勧めします。多段階ビルドもイメージを小さく保つのに役立つかもしれません。

イメージサイズを小さく保つには、diveを使ってDockerイメージのレイヤーを分析し、さらなる肥大化の原因を特定することを検討してください。

場合によっては、イメージからファイルを削除するのが難しいかもしれません。 このような場合、Zstandardを使ってファイルや大きなディレクトリを圧縮することを検討してください。 Zstandardには様々な圧縮レベルがあり、解凍速度にほとんど影響を与えずにイメージのサイズを小さくすることができます。 イメージが起動するとすぐに、圧縮されたディレクトリを自動的に解凍すると便利です。 Dockerイメージの/etc/bashrc 、または特定のユーザの$HOME/.bashrcにステップを追加することでこれを実現できます。後者のオプションを選択した場合は、bashログインシェルを起動するようにエントリーポイントを変更することを忘れないでください。

以下はその例です:

画像タグ

Docker Official Imagesプロジェクトで文書化されているように、バージョン番号タグにはエイリアスを付けることが強く推奨されています。Docker Tagging: Best practices for tagging and versioning Docker imagesも参照してください。

コマンドライン

スキャナは、環境変数を入力として受け取り、(ジョブ定義に基づいて)レポートとしてアップロードされるファイルを生成するコマンドラインツールです。 また、標準出力と標準エラーストリームにテキスト出力を生成し、ステータスコードで終了します。

変数

すべてのCI変数は環境変数としてスキャナに渡されます。 スキャンされたプロジェクトは定義済みのCI変数によって記述されます。

SASTと依存関係のスキャン

SASTと依存関係スキャンのスキャナは、CI_PROJECT_DIR 変数で指定されたプロジェクトディレクトリ内のファイルをスキャンする必要があります。

コンテナの脆弱性スキャン

公式のContainer Scanning for GitLabと一致させるために、スキャナはDockerイメージの名前とタグがそれぞれCI_APPLICATION_REPOSITORYCI_APPLICATION_TAGで与えられるものをスキャンしなければなりません。

提供されない場合、CI_APPLICATION_REPOSITORY のデフォルトは$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUGとなり、これは事前に定義された CI 変数の組み合わせです。CI_APPLICATION_TAG のデフォルトはCI_COMMIT_SHAとなります。

スキャナはDockerレジストリで変数DOCKER_USERDOCKER_PASSWORDを使用して署名する必要があります。これらが定義されていない場合、スキャナはデフォルト値としてCI_REGISTRY_USERCI_REGISTRY_PASSWORD を使用する必要があります。

設定ファイル

スキャナはCI_PROJECT_DIR を使って特定の設定ファイルを読み込むことができますが、設定はファイルではなく環境変数として公開することをお勧めします。

出力ファイル

GitLab CI/CD にアップロードされたアーティファクトと同様に、スキャナによって生成された Secure レポートはCI_PROJECT_DIR 環境変数によって指定されたプロジェクトディレクトリに書き込まれなければなりません。

出力ファイルにはスキャンの種類にちなんだ名前を付け、接頭辞としてgl- を使用することをお勧めします。 Secure レポートはすべて JSON ファイルであるため、ファイル拡張子として.json を使用することをお勧めします。例えば、依存関係スキャンレポートの推奨ファイル名はgl-dependency-scanning.jsonです。

ジョブ定義のartifacts:reports キーワードは、セキュリティレポートが書き込まれるファイルパスと一致している 必要があります。インスタンスンスンス、依存関係スキャンアナライザがそのレポートを CI プロジェクトディレクトリに書き込み、このレポートファイル名がdepscan.jsonである場合、artifacts:reports:dependency_scanningdepscan.jsonに設定する必要があります。

終了コード

POSIXの終了コード標準に従って、スキャナは成功の場合は0、それ以外の場合は1から255までの任意の数値で終了します。 成功には、脆弱性が見つかった場合も含まれます。

Docker-in-Docker特権モードを使用してスキャンジョブを実行する場合、以下の標準終了コードを予約します。

オーケストレーター終了コード 説明
3 適合する分析装置がありません
4 プロジェクトディレクトリ空
5 互換性のあるDockerイメージがありません。

ロギング

スキャナーはエラーメッセージや警告をログに記録し、ユーザーがCIスキャンジョブのログを見ることで設定ミスやインテグレーションに関する問題を簡単に調査できるようにする必要があります。

スキャナは、ANSIエスケープコードを使用して、Unix標準出力および標準エラーストリームに書き込むメッセージに色を付けることができます。 エラーを報告する場合は赤、警告を報告する場合は黄、通知を報告する場合は緑を使用することをお勧めします。 また、エラーメッセージの先頭に[ERRO]、警告の先頭に[WARN]、通知の先頭に[INFO]を付けることをお勧めします。

ログレベル

SECURE_LOG_LEVEL 例えば、SECURE_LOG_LEVELerrorに設定されている場合、infowarnメッセージはスキップされます:

  • fatal
  • error
  • warn
  • info
  • debug

デバッグ時に有用な冗長ロギングには、debug レベルを使用することをお勧めします。SECURE_LOG_LEVEL のデフォルト値は、infoに設定する必要があります。

共通 logutil パッケージ

goとcommonを使用している場合、logrusとcommonのlogutilパッケージを使用して、logrus用のフォーマッタを設定することをお勧めします。logutilのREADME.mdを参照してください。

レポーター

レポートは、脆弱性と可能な改善策を組み合わせたJSONドキュメントです。

このドキュメントでは、レポート JSON フォーマットの概要と、インテグレータがフィールドを設定するのに役立つ推奨事項や例を示します。 このフォーマットは、SAST依存関係スキャンコンテナスキャンのドキュメントで広く説明されています。

レポートJSONフォーマットのDASTバリアントは、現時点では文書化されていません。

バージョン

このフィールドは、使用しているレポート・スキーマのバージョンを指定します。 使用する特定のバージョンについては、各スキャナのページを参照してください。

脆弱性

レポートのvulnerabilities フィールドは脆弱性オブジェクトの配列です。

身分証明書

このidフィールドは脆弱性の一意な識別子 idですid修復オブジェクトから修正された脆弱性を参照するために使用されます。 UUID を生成し、それを idフィールドのid値として使用することを推奨します id

カテゴリー

category フィールドの値は、レポート・タイプと一致します:dependency_scanningcontainer_scanningsastdast

スキャナー

scanner フィールドは、人間が読むことのできるname と技術的なid. id他のインテグレーターが提供する他のスキャナーと衝突してはなりません。

名前、メッセージ、説明

name 」と「message 」には、脆弱性の簡単な説明が記載されています。 「description 」には、より詳細な情報が記載されています。

name フィールドはコンテクストフリーで、脆弱性が発見された場所に関する情報を含みませんが、message はその場所を繰り返す可能性があります。

視覚的な例として、このスクリーンショットは、パイプラインビューの一部として脆弱性を表示するときに、これらのフィールドが使用される場所を強調しています。

Example Vulnerability

例えば、依存関係スキャンによって報告された脆弱性のmessage は、脆弱性の依存関係の情報を提供しますが、これは脆弱性のlocation フィールドと冗長です。name フィールドが望ましいですが、message フィールドは、脆弱性のタイトルからコンテキスト/場所を削除できない場合に使用されます。

説明のために、依存性スキャナによって報告された脆弱性オブジェクトの例を示します。messagelocation フィールドを繰り返します:

{
    "location": {
        "dependency": {
            "package": {
            "name": "debug"
          }
        }
    },
    "name": "Regular Expression Denial of Service",
    "message": "Regular Expression Denial of Service in debug",
    "description": "The debug module is vulnerable to regular expression denial of service
        when untrusted user input is passed into the `o` formatter.
        It takes around 50k characters to block for 2 seconds making this a low severity issue."
}

脆弱description 性オブジェクトの他のフィールドを繰り返してはなりません。特に descriptionlocation (何が影響を受けるか)やsolution (どのようにリスクを軽減するか)を繰り返してはなりません。

解決方法

solution フィールドを使用して、特定された脆弱性の修正方法やリスクの軽減方法をユーザーに指示することができます。エンドユーザーはこのフィールドを操作しますが、GitLab は自動的にremediations オブジェクトを処理します。

識別子

identifiers 配列には、検出された脆弱性が記述されています。 identifier オブジェクトのtypevalue フィールドは、2 つの識別子が同じかどうかを判別するために使用されます。 ユーザー・インターフェースは、オブジェクトのnameurl フィールドを使用して識別子を表示します。

GitLabスキャナが既に定義している識別子を再利用することをお勧めします:

識別子 タイプ 値の例
CVE cve CVE-2019-10086
CWE cwe CWE-1026
オーエスブイディー osvdb OSVDB-113928
米海軍 usn USN-4234-1
WASC wasc WASC-19
アールエイチエスエー rhsa RHSA-2020:0111
エルサ elsa ELSA-2020-0085

上記の汎用識別子は共通ライブラリで定義されており、GitLabがメンテナーとして管理しているアナライザーで共有されています。 必要であれば、新しい汎用識別子を貢献することができます。 アナライザーはベンダー固有の識別子や製品固有の識別子を生成することがありますが、これらは共通ライブラリには属しません。

identifiers 配列の最初の項目はプライマリ識別子と呼ばれます。 プライマリ識別子は特に重要で、新しいコミットがリポジトリにプッシュされた際に脆弱性を追跡するために使用されます。 また、CWEWASCを除いて、同じコミットで報告された重複する脆弱性をマージするためにも識別子が使用されます。

所在地

location は脆弱性が検出された場所を示します。場所の形式はスキャンの種類によって異なります。

GitLab 内部では、location のいくつかの属性を抽出してlocation fingerprintを生成します。これは、新しいコミットがリポジトリにプッシュされたときに脆弱性を追跡するために使われます。 location fingerprint を生成するために使われる属性はスキャンの種類によっても異なります。

依存関係の脆弱性スキャン

依存関係スキャニングの脆弱性のlocation は、dependencyfileから構成されます。dependency オブジェクトは、影響を受けるpackage と依存関係versionを記述します。package は、影響を受けるライブラリ/モジュールのname を埋め込みます。file は、影響を受ける依存関係を宣言する依存関係ファイルのパスです。

例えば、以下は npm パッケージhandlebarsのバージョン4.0.11 に影響する脆弱性に関するlocation オブジェクトです:

{
    "file": "client/package.json",
    "dependency": {
        "package": {
            "name": "handlebars"
        },
        "version": "4.0.11"
    }
}

この影響を受ける依存関係は、npmまたはyarnによって処理される依存関係ファイルであるclient/package.json

依存関係スキャニングの脆弱性の位置フィンガープリントはfile とパッケージnameを組み合わせます。

コンテナの脆弱性スキャン

依存性スキャニングと同様に、コンテナ・スキャニング脆弱性のlocation にはdependencyfileがあります。 また、operating_system フィールドもあります。

例えば、Debian パッケージglib2.0のバージョン2.50.3-2+deb9u1 に影響する脆弱性に関するlocation オブジェクトです:

{
    "dependency": {
        "package": {
            "name": "glib2.0"
        },
    },
    "version": "2.50.3-2+deb9u1",
    "operating_system": "debian:9",
    "image": "registry.gitlab.com/example/app:latest"
}

影響を受けるパッケージは Docker イメージregistry.gitlab.com/example/app:latestをスキャンした際に見つかります。 Docker イメージはdebian:9 (Debian Stretch) をベースにしています。

コンテナスキャン脆弱性の位置フィンガープリントはoperating_system とパッケージnameを組み合わせたもので、これらの属性は必須です。image も必須です。他の属性はすべて任意です。

SAST

SAST 脆弱性のlocation は、filestart_line フィールドを持たなければなりません。それぞれ、影響を受けるファイルのパスと影響を受ける行番号を与えます。また、end_lineclassmethodを持つこともあります。

例えば、src/main/java/com/gitlab/example/App.java41 行目、com.gitlab.security_products.tests.App の Java クラスのgenerateSecretToken メソッドで見つかったセキュリティ欠陥のlocation オブジェクトです:

{
    "file": "src/main/java/com/gitlab/example/App.java",
    "start_line": 41,
    "end_line": 41,
    "class": "com.gitlab.security_products.tests.App",
    "method": "generateSecretToken1"
}

SAST 脆弱性のロケーション・フィンガープリントは、filestart_lineend_lineを組み合わせたものであり、これらの属性は必須です。 その他の属性はすべて任意です。

脆弱性の追跡とマージ

ユーザーは脆弱性についてフィードバックすることができます:

  • 自分たちのプロジェクトに当てはまらなければ、脆弱性を否定することもあります。
  • 可能性のある脅威があれば、脆弱性のイシューを作るかもしれません。

GitLabは脆弱性を追跡するので、新しいGitコミットがリポジトリにプッシュされたときにユーザーのフィードバックが失われることはありません。 脆弱性は3つの属性の組み合わせを使って追跡されます:

現在 GitLab では、新しい Git コミットがプッシュされたときに脆弱性の場所が変わると、その脆弱性を追跡することができません。 例えば、影響を受けるファイルの名前が変更されたり、影響を受ける行が下に移動したりすると、SAST 脆弱性に関するユーザーからのフィードバックが失われます。 これは、イシュー#7586で対処されています。

場合によっては、同じ CI パイプラインで実行された複数のスキャンの結果、脆弱性の位置と識別子を使用して自動的にマージされる重複が生じます。 2 つの脆弱性は、同じ位置のフィンガープリントと少なくとも 1 つの識別子を共有する場合、同じとみなされます。 2 つの識別子は、同じtypeidを共有する場合、同じとみなされます。 CWE と WASC 識別子は、脆弱性の欠陥のカテゴリを記述するが、特定のセキュリティ欠陥を記述しないため、考慮されません。

厳しさと自信

severity 」フィールドは、脆弱性がソフトウェアにどの程度影響するかを記述し、「confidence 」フィールドは、脆弱性の評価がどの程度信頼できるかを記述します。「深刻度」は、セキュリティダッシュボードで脆弱性を並べ替えるために使用されます。

深刻度はInfo からCriticalまでの範囲ですが、次のようにすることもできますUnknown。 有効な値は次のとおりです: Unknown,Info,Low,Medium,High, またはCritical

信頼度の範囲はLow からConfirmedまでですが、UnknownExperimental 、あるいは脆弱性をIgnore 無視することもできます。 有効な値は次のとおりです: IgnoreUnknownExperimentalLowMediumHigh、またはConfirmed

Unknown の値は、実際の値を決定するためのデータが利用できないことを意味します。 したがって、highmediumlowの可能性があり、調査が必要です。 利用可能なSASTアナライザと現在利用可能なデータのChartを提供しています。

修復

レポートのremediations フィールドは、修復オブジェクトの配列です。各修復は、脆弱性の セットを自動的に修正するために適用できるパッチを記述しています。

以下は、改善策を含むレポートの例です。

{
    "vulnerabilities": [
        {
            "category": "dependency_scanning",
            "name": "Regular Expression Denial of Service",
            "id": "123e4567-e89b-12d3-a456-426655440000",
            "solution": "Upgrade to new versions.",
            "scanner": {
                "id": "gemnasium",
                "name": "Gemnasium"
            },
            "identifiers": [
                {
                  "type": "gemnasium",
                  "name": "Gemnasium-642735a5-1425-428d-8d4e-3c854885a3c9",
                  "value": "642735a5-1425-428d-8d4e-3c854885a3c9"
                }
            ]
        }
    ],
    "remediations": [
        {
            "fixes": [
                {
                    "id": "123e4567-e89b-12d3-a456-426655440000"
                }
            ],
            "summary": "Upgrade to new version",
            "diff": "ZGlmZiAtLWdpdCBhL3lhcm4ubG9jayBiL3lhcm4ubG9jawppbmRleCAwZWNjOTJmLi43ZmE0NTU0IDEwMDY0NAotLS0gYS95Y=="
        }
    ]
}

概要

summary フィールドは、脆弱性の修正方法の概要です。 このフィールドは必須です。

脆弱性の修正

fixes フィールドは、修復によって修正された脆弱性を参照するオブジェクトの配列です。fixes[].id には、修正された脆弱性の一意の識別子が含まれます。 このフィールドは必須です。

差分

diff フィールドは、git applyと互換性のある、base64 エンコードされた修復コードの差分です。 このフィールドは必須です。

制限事項

コンテナの脆弱性スキャン

コンテナスキャンには現在、このような制限があります:

  • セキュリティダッシュボードは複数のイメージからのスキャン結果を表示できますが、複数の脆弱性が同じフィンガープリントを持つ場合、その脆弱性の最初のインスタンスのみが表示されます。 私たちはこの制限を削除するために取り組んでいます。 イシューの進捗状況はコンテナスキャンの場所フィンガープリントの変更で確認できます。
  • 異なるスキャナがそれぞれ同じ脆弱性をレポーターすることがあり、その結果、発見が重複することがあります。