開発に使える脆弱性スキャンツール

この記事は、 NTT Communications Advent Calendar 2022 7日目の記事です。

はじめに

こんにちは、イノベーションセンター所属の志村と申します。

「Metemcyber」プロジェクトで脅威インテリジェンスに関する内製開発や、「NA4Sec」プロジェクトで攻撃インフラの解明・撲滅に関する技術開発を担当しています。

今回は「開発に使える脆弱性スキャンツール」をテーマに、GitHub Dependabot, Trivy, Grypeといったツールの紹介をさせていただきます。

脆弱性の原因とSCAによるスキャン

現在のソフトウェア開発は、多くのOSSを含む外部のソフトウェアに依存しています。Python、Go、npm など多くの言語は、様々なソフトウェアをパッケージとして利用できるエコシステムを提供しており、この仕組みを利用してOSSなどのコンポーネントをソフトウェアに組み込んで活用するのが一般的です。

外部パッケージを利用することで効率的な開発が可能になる一方で、それらに含まれる脆弱性の影響を受けてしまうリスクが増加しています。Snyk社のレポート1によれば、オープンソースのソフトウェアの脆弱性の約80%は間接的に依存しているパッケージによりもたらされるとされています。 そのため直接的に利用しているパッケージだけでなく、それらの依存先のパッケージまで含めて把握し、脆弱性がないか管理する必要があります。

ソフトウェアの依存関係を解析する手段として、SCA (Software Composition Analysis) と呼ばれるツールの活用が挙げられます。 SCAを活用してソフトウェアの依存関係から脆弱性を発見し、それらに対して適切に処置をする、ということが複雑化するソフトウェア開発における脆弱性対応では重要です。

SCA型のスキャンツールの紹介

脆弱性対策に使えるSCAは有償のものも含めて多くありますが、今回は無償で使える以下の3つを紹介します。

  • Dependabot
  • Trivy
  • Grype

GitHub Dependabot

概要

GitHub DependabotはRepositoryの構成要素を分析し、脆弱な依存関係を発見・通知するGitHubの機能です。 GitHub Dependabotの機能はPrivate Repoであっても無償で利用できるため、GitHubを利用しているならば万人にお勧めできます。2

データソース

GitHub Dependabotは、既知の脆弱性やマルウェアの情報が含まれるデータベースである GItHub Advisory Database に含まれる脆弱性が通知されます。NVDや各種言語のセキュリティアドバイザリの情報がデータソースとなっています。

GitHub Advisory Databaseの情報はGitHub独自の脆弱性IDで管理され、脆弱性の詳細や影響を受けるバージョン、修正済みのバージョンなどの情報が含まれています。

GitHub Advisory Databseの中でも、GitHubがサポートするエコシステムにマッピングされた脆弱性/マルウェアの情報をGitHub-reviewed Advisory と呼び、この情報がDependabot で通知されます。 GitHub-reviewed AdvisoryはGitHub社によるレビューやパッケージシステムとの対応づけが完了しており、影響を受けるバージョンや修復済みのバージョンの情報も含まれているため、対応策定に役立てることができます。

本ブログ執筆時点で対応しているエコシステムは以下の通りです。

使い方

Dependabot を利用するには、Repositoryページの Settings -> Code security and analysis にアクセスし、 Dependency graph および Dependabot 関連の機能をEnableにする必要があります。

GitHub Dependency GraphはRepositoryの中の言語依存ライブラリの管理ファイルなどを解析し、依存しているライブラリの一覧を抽出する機能です。 Dependency Graph で解析できる対象はAbout the dependency graph のドキュメントを参照してください。

Dependency Graphを有効にするとソースコードが解析され、依存パッケージが確認できる様になります。 解析結果はRepositoryページの InsightDependency Graph にアクセスすることで確認できます。

Dependabot alertsを有効化すると、GitHub Advisory Databaseに関連する情報が登録されると通知される様になります。 Dependabot alertsの情報は、Repositoryの Security -> Dependabot から確認できます。 Dependabot security updates を有効化していると、パッケージのバージョンを脆弱性修正バージョンに上げるPull Request が自動的に発行されます。

Trivy

概要

TrivyはGo製のツールで、Linux, Windows, Mac いずれの環境でも動作します。

Trivyはセキュリティのアーミーナイフをうたっており、以下のような多様な機能を有しています。

  • 脆弱性スキャン
    • OSパッケージ、言語のパッケージをスキャンし、脆弱性を発見する
  • Secret スキャン
    • 鍵ファイル (AWSキー、slackキーなど) が存在してないかを確認する
  • Configスキャン
    • Terraform 設定ファイルやDockerfileなどをスキャンし、セキュリティ上問題になりうる設定が含まれてないかチェックする

いずれの機能もソフトウェアの安全性を高めるために有効ですが、今回は脆弱性スキャンについて取り上げます。

Trivyの脆弱性スキャンはファイルシステムなどを解析し、依存しているOSや言語のパッケージを抽出し、既存の脆弱性情報とマッチングすることで脆弱性の有無を判断します。

Trivyが脆弱性の有無を解析できる対象は以下の通りです。

  • OSパッケージ
    • apt, yum などでインストールしたOSパッケージ情報を抽出し、脆弱性を表示する
  • 言語パッケージ
    • 言語の依存ライブラリの管理ファイル (pacakge-lock.json, pipenv.lock など) をスキャンして、パッケージのバージョンを取得し、脆弱性を表示する

データソース

Trivyの脆弱性スキャンは、Trivyが収集したOSや言語パッケージのバージョン情報を、Trivyの脆弱性DBとマッチングすることで行われます。

Trivyが利用する脆弱性DBは trivy-db というツールで作成されており、DB自体の更新もこのtrivy-db RepositoryのGitHub Actionsで実行されています。 このDBは6時間ごとに更新されており、最新の脆弱性情報を参照できます。

Trivyがどのようなデータソースから情報を収集しているかは、Trivyドキュメントの Data Sources やtrivy-dbのソースコードから読み解けます。前述したGitHub Advisory Database もデータソースに含まれています。

主なデータソースは以下の通りです。

使い方

Trivyの脆弱性スキャンの対象は以下の通りです。

  • コンテナイメージ
  • ファイルシステム
  • GIt Repo

コンテナイメージのスキャン

コンテナイメージをスキャンする場合は、 trivy imageコマンドを利用します。

trivy image python:3.4-alpine 

出力は以下の様になります。

イメージスキャンは コンテナイメージを指定して、その中に含まれるOSパッケージや言語ライブラリの脆弱性をスキャンできます。

ファイルシステムのスキャン

ファイルシステムスキャンをする場合は、 trivy fsコマンド もしくは trivy rootfsコマンドを利用します。

trivy fs /path/to/project
trivy rootfs /

ファイルシステムをスキャンして、OSパッケージのファイルや言語のパッケージの設定ファイル (npm の package-lock.json など) を解析してバージョン情報などを抽出し、既知の脆弱性が発見された場合に通知します。

fsコマンドは主にローカルにあるプロジェクトのスキャン、rootfsコマンドはRootfsのスキャン (コンテナ内部でスキャンや、コンテナイメージのファイルのスキャンなど) での利用を想定されています。参考

例えばPythonでは、fsコマンドだと Pipfile.lock などのパッケージの管理ファイルからパッケージとバージョンを抽出するのに対し、rootfsコマンドでは site-packages/ ディレクトリなどをスキャンして実際にインストールされているパッケージとバージョンを抽出する、という挙動の違いがあります。開発中のプロジェクトのソースコードをスキャンしたい場合はfsコマンド、コンテナやホストマシン内部など今動いている環境のスキャンはrootfs、の様に使い分けるのが良いでしょう。

fsコマンドとrootfsコマンドのスキャン対象の違いについては Trivyのドキュメント なども参照してください。

スキャンの挙動設定

Configファイル (trivy.yaml)を利用することで、スキャンの挙動を設定できます。設定可能な内容は ドキュメント を参照してください。

また .trivyignore に脆弱性ID (CVE-ID) を記載することで、その脆弱性を無視できます。

スキャンフォーマットの指定

Trivyはスキャン結果の出力フォーマットが指定可能です。デフォルトのtableフォーマットはスキャン対象・脆弱性・Severityなどが表示され、視認性が高いフォーマットです。

それ以外にもjsonフォーマットなどを指定できます。 jsonフォーマットで出力すると、tableフォーマットでは出力されないスキャンターゲットのファイルパス (PkgPath)などの情報が表示されます。 rootfs スキャンでマシン全体をスキャンした際など、どこで脆弱性が検知されたかわからない、という場合に有効です。 json オプションを使用する場合は --output オプションと組み合わせてファイルに出力するのが良いでしょう。

CIへの組み込み

trivy-action を利用することで、GitHub Actions上でのTrivy実行が可能です。

GitHub Actionsでの設定方法は以下のようになります。以下の例では severity などの情報をworkflow上で記載していますが、 Trivy Configファイルに必要な設定を記載してRepoに配置しておき、そのファイルを trivy-config で参照することもできます。 (ただし trivy-config を指定すると、 scan-typescan-ref 以外のオプションは無視されるため注意してください。)

name: build
on:
  push:
    branches:
      - master
  pull_request:
jobs:
  build:
    name: Build
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Build an image from Dockerfile
        run: |
          docker build -t docker.io/my-organization/my-app:${{ github.sha }} .
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'docker.io/my-organization/my-app:${{ github.sha }}'
          format: 'table'
          exit-code: '1'
          ignore-unfixed: true
          vuln-type: 'os,library'
          severity: 'CRITICAL,HIGH'

SBOMの出力、SBOMファイルのスキャン

Trivyはスキャン結果からSBOMを出力したり、SBOMファイルから脆弱性スキャンを行うこともできます。 SBOM活用の詳細については12/1のアドベントカレンダーもぜひ参照してください。

TrivyでSBOMファイルを作成する場合は、 --format で出力したいSBOMフォーマット (spdx-json or cyclonedx) を指定します。

trivy image --format spdx-json --output result.json alpine:3.15

TrivyでSBOMファイルをスキャンする場合は、trivy sbomコマンドでSBOMファイルを指定します。

trivy sbom /path/to/spdx.json

Grype

概要

GrypeはTrivyより後発のセキュリティスキャンツールです。SBOMツールであるSyftと連携して動作し、スキャン結果のSBOMファイルへの出力や、SBOMファイルを利用した脆弱性スキャンが可能です。

データソース

Grypeのデータソースは Github で参照できます。 基本的にはTrivyと類似したデータソースになっています。

Grypeのスキャン

Gypeは以下のスキャンに対応しています。

  • コンテナイメージ
  • OSパッケージ
  • 言語のパッケージ
  • SBOMファイルのスキャン
    • CycloneDX, SBOM, syft形式のスキャンが可能

Grypeのスキャンは以下の様に行います。

grype python:3.4-alpine 

出力は以下の様になります。

$ grype python:3.4-alpine                     
 ✔ Vulnerability DB        [updated]
 ✔ Parsed image            
 ✔ Cataloged packages      [31 packages]
 ✔ Scanned image           [106 vulnerabilities]
NAME          INSTALLED   FIXED-IN   TYPE    VULNERABILITY        SEVERITY 
busybox       1.29.3-r10             apk     CVE-2021-42379       High      
busybox       1.29.3-r10             apk     CVE-2021-42376       Medium    
busybox       1.29.3-r10             apk     CVE-2021-42385       High      
busybox       1.29.3-r10             apk     CVE-2021-42378       High      
busybox       1.29.3-r10             apk     CVE-2021-42381       High      
busybox       1.29.3-r10             apk     CVE-2021-42380       High        

SBOMファイルを元にスキャンする場合は以下の様になります。Syftによって生成されたSBOMファイルなら正確な検知が可能になっています。

grype sbom:./spdx.json

脆弱性スキャンツールの使い分け

ここまでGitHub Dependabot、Trivy、Grypeについて紹介しました。 これらのツールを活用することで、脆弱性の発見と対処が容易になります。

どの様に脆弱性スキャンツールを使い分けていくかはプロジェクトの状況などによりますが、個人としては以下をお勧めします。

  • GitHubを利用しているなら、Dependabot を利用してソースコードの脆弱性を検知・対応する
  • コンテナイメージなどのソースコードのみではスキャンできない対象をCI/CDのプロセス内で検知したい場合、trivy-action などを活用することでTrivyをCI/CD に組み込む
  • デプロイ先の環境 (VM、コンテナなど) のスキャンを実施したい場合は、Trivyでrootfs や imageスキャンを実施する
  • SBOMを用いた構成管理および脆弱性スキャンを実施したいなら、Syft + Grype の組み合わせで運用する

開発にGitHub を利用しているなら、まずはDependabotを有効化してしまって良いと思います。 GitHub Advisory Databseは優秀な脆弱性DBであり、その内容を通知してくれるDependabot を有効化することで迅速な対応が可能になります。

GitHub Dependabotではカバーできないコンテナイメージのスキャンなどを実施したいなら、TrivyをCI/CDに組み込むのがお勧めです。 Trivyはスキャンが高速なため、開発への影響を最小限に抑えつつセキュリティレベルを向上させることが期待できます。

GrypeはSyft との連携が念頭に置かれています。 Syftは強力なSBOMジェネレータのため、SBOMの生成にSyftを使っているのならGrypeと合わせて運用するのが良いと思われます。

CI/CDで実現する継続的な脆弱性スキャン

Metemcyber PJではDependabotとTrivyを開発に組み込んでいます。 今回はTrivyを使ったCICDを例として取り上げ、どのようにCI/CD で継続した脆弱性スキャンを実現するかを紹介したいと思います。

trivy-actionの活用

私たちはtrivy-actionを利用して、GitHub Actionを利用した脆弱性のスキャンを実施しています。 以下はtrivy-actionを利用して、Pull Request 時と mainへのpush時にfsスキャンを実施し、Trivyスキャンで脆弱性が発見されたらGitHub Actionsをfailさせる例 (一部抜粋) です。

name: Pipenv CI

on:
  pull_request:
    branches:
      - main
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  build:
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3
      - name: Run Trivy vulnerability scanner in fs mode
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: './api'
          trivy-config: trivy.yaml

Actionsで参照しているtrivy.yamlは以下の様になります。

debug: true
exit-code: 1
severity:
  - HIGH
  - CRITICAL

上記のようなGitHub Actionsとtrivy.yamlを用意しておくことで、Pull Requestを行うと自動的にTrivyスキャンが実施されます。 trivy-actionはデフォルトで脆弱性スキャンとSecretスキャンを行うため、ソースコード内にトークンなどの機密情報が含まれていないかのチェックも可能になっています。

trivy.yamlseverityを用いると、スキャンの対象とする脆弱性の脅威レベルを設定できます。 上記の例ではTrivy基準でHigh以上の脆弱性を検知すると、exit-code: 1 の設定によりActionがfailし、脆弱性を発見できる仕組みになっています。

Pipenvの採用

上記の仕組みを実現するために、Pythonのパッケージマネージャーとして pipenv を採用しています。

Pythonで環境を構築するには、requirements.txt にインストールしたいパッケージを記述しておき、pipでインストール方法もあります。

pip install -r requirements.txt

しかし私たちは以下の理由でpipenvを採用しています。

  1. 依存するパッケージを Pipfile.lock で明確化してTrivyでスキャンできる
  2. 開発環境でしか利用しないパッケージを分離し、Trivyのスキャンの対象外にできる

Trivyのfsスキャンでは、requirements.txtに記述されていない、間接的に依存するパッケージはfsスキャンで検知できません。 そのためrequirements.txtに直接参照するパッケージのみを記述している場合、実際にインストールされているパッケージの脆弱性を見逃すことがあります3。 pipenvでは実際にインストールされるパッケージがPipfile.lockに記述され、Trivyのfsスキャンで検知可能となります。

またpipenvでは、--devオプションを利用することで、開発環境のみで使うパッケージを別枠でインストールできます。

pipenv install --dev autopep8

Trivyではdevオプションでインストールしたパッケージはスキャン対象外となります。これにより、プロダクション環境に存在する脆弱性のみを検知することが可能になります。

12/1のアドベントカレンダー でも言及がありましたが、脆弱性対応を適切に行うにはパッケージマネージャーの選定も重要になります。 pipenv を利用することで、実際にインストールされるパッケージ全てのスキャンや、devDependencies のスキャン対象からの除外を実現できるので、Trivy を活用する場合は採用を検討することをお勧めします。

まとめ

本記事では、DependabotやTrivy、Grypeといったスキャン系のツールを紹介しました。 ツールごとに強みがあるので、開発体制などに応じて使い分けていくことでセキュリティの向上が期待できます。

ただし、これらのツールを使えば全ての問題が解決するかというとその様なことはなく、スキャン結果をどのように運用に組み込むかは別途考えなくてはなりません。 脆弱性が発見された際に即時アップデートをするか、それとも通常のリリースサイクルで対応するのか。 修正バージョン自体存在しない脆弱性が発見された場合どうするかなど、実運用ではさまざまな課題に直面します。 これらは開発しているソフトウェアの性質や、ソフトウェアが取り扱う情報資産の重要度などに合わせて適切に決定していく必要があります。

今回紹介した脆弱性スキャンツールは、CICDを活用した開発プロセスへ導入し、早期に脆弱性を発見することで効果を発揮します。 そのためセキュリティの視点だけでなく、利用するパッケージマネージャーや開発・デプロイのプロセスといった視点も含めて改善していくことで、効果的な脆弱性対応のプロセスを実現できるでしょう。

宣伝

私たちはMetemcyber という、セキュリティインテリジェンスやTrivyなどのツールの結果を管理し、セキュリティアクションを実施していくツールを現在開発中です。 今後 MetemcyberのTwitterアカウント にて情報発信していく予定です。このアカウントでは セキュリティインテリジェンスの発信 などの活動も行っているので、ぜひフォローしてください。

それでは、明日もお楽しみに。


  1. https://go.snyk.io/rs/677-THP-415/images/State%20Of%20Open%20Source%20Security%20Report%202020.pdf
  2. https://github.co.jp/pricing.html
  3. pip freezeを利用してインストールされているパッケージを全て出力することでfsスキャンによる検知が可能になりますが、直接依存するパッケージと間接的に依存するパッケージが混在してしまいます。
© NTT Communications Corporation All Rights Reserved.