Trivyを使用して脆弱性検知とSBOM出力をやってみる

記事タイトルとURLをコピーする

本記事はサーバーワークス Advent Calendar 2023 の8日目の記事です。

qiita.com

こんにちは。アプリケーションサービス部の兼安です。
今回はセキュリティのお話です。
日々ソフトウェアの改善を行う中で、スピードとセキュリティの両立にお悩みの方も多いのではないでしょうか。
今回は、脆弱性を管理するSBOMという仕組みを、OSSのTrivyというツールを使ってご紹介します。

trivy.dev

本記事のターゲット

本記事は、以下のような方を対象としています。

  • SBOMが何なのか知りたい方
  • どうやったらSBOMを利用できるのか知りたい方

本記事執筆に用いた環境

  • macOS 14.11
  • Trivy 0.48.0

SBOMとは

SBOMとは、Software Bill of Materialsの略称です。
SBOMは、ソフトウェアコンポーネントやそれらの依存関係をリスト化したものです。
国際的な標準規格がいくつかあります。
以下は代表例です。

規格が整っているので、機械処理可能になっているのが特徴です。

SBOMを使用する目的は、以下のものがあります。

  • ソフトウェアコンポーネントの脆弱性管理
  • ソフトウェアコンポーネントのライセンス管理
  • 管理コストの削減
  • 脆弱性の少ない製品が増えることで、サイバー衛生を向上させる

4つ目は、SBOMに対応したソフトウェアが増えたら暁には、の話ですね。
本記事では、この中から脆弱性管理についてご紹介します。

参考
経済産業省 - ソフトウェア管理に向けたSBOM(Software Bill of Materials)の導入に関する手引 Ver. 1.0

SBOMを取り巻く状況

ソフトウェア開発におけるSBOMの対応状況は、業界ごとによって異なります。
下記の資料を見る限り、現状医療業界がもっとも進んでいます。
公共性の強い業界ほど、SBOMの議論が進んでいるようです。

基準・ガイドラインに関連付いた業界の解説文書、業界セミナーなどでSBOMガイダンスの参照・推奨を受けデファクト化を推進。 (規制当局・認証機関におけるSBOMガイダンスの活用促進)

引用元
「本事業の成果物を活用したSBOM普及促進策案」(P33)より
経済産業省 - サイバー・フィジカル・セキュリティ確保に向けたソフトウェア管理手法等検討タスクフォースの検討の方向性

医療業界では、 今年の春に薬事法の改正がありました。
ここでSBOMが必要になることの記載があります。

(4) JIS T 81001―5―1の箇条8のソフトウェア構成管理プロセスについて 構成管理プロセスは、当該医療機器のソフトウェア部品表(SBOM)を適切に作成することによって確認すること。

引用元
厚生労働省 - 医療機器の基本要件基準第12条第3項の適合性の確認について

この文書には、1年間の経過措置期間が設定されるとあります。
従って、2024年以降だと医療に関わるシステムには 私自身は医療業界に携わっていないので、主観しか述べることはできませんが、医療業界では2024年に向けてSBOMの対応が進んでいると考えられます。

今後、各業界においてSBOMの必要性が高まることが予想されます。
来るべき時に備えて、少しずつ準備を進めていくのがよいと思います。

Trivyで脆弱性を検知する

Trivyは、脆弱性を検知するOSSのツールです。
Trivyは、コンテナイメージやアプリケーションのソフトウェアコンポーネントを検知し、SBOMとして出力することができます。

早速やってみましょう。
Trivyの公式ドキュメントに従ってインストールします。
aquasecurity.github.io

Flaskのプロジェクトを作ります。 Pipfileは以下のようになります。

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
flask = "*"

[dev-packages]

[requires]
python_version = "3.11"

これに基づきライブラリをインストール後、Trivyでスキャンをかけます。
今回は簡単にするため、コンテナを使わずファイルシステムに対してスキャンをかけます。

trivy fs ./

aquasecurity.github.io

fsがファイルシステムのスキャンを意味します。
./はカレントディレクトリを意味します。

$trivy fs ./
2023-12-06T20:12:51.087+0900    INFO    Loaded trivy.yaml
2023-12-06T20:12:51.104+0900    INFO    Vulnerability scanning is enabled
2023-12-06T20:12:51.104+0900    INFO    Secret scanning is enabled
2023-12-06T20:12:51.104+0900    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-12-06T20:12:51.104+0900    INFO    Please see also https://aquasecurity.github.io/trivy/v0.48/docs/scanner/secret/#recommendation for faster secret detection
2023-12-06T20:12:51.123+0900    INFO    Number of language-specific files: 1
2023-12-06T20:12:51.123+0900    INFO    Detecting pipenv vulnerabilities...

特に何も出ずに終わりました。
脆弱性は見つからなかったようです。
では、Flaskのバージョンを脆弱性が報告されているバージョンにして再試行してみます。 脆弱性はこちらを使用します。

CVE-2023-30861 Flask vulnerable to possible disclosure of permanent session cookie due to missing Vary: Cookie header

Pipfileを以下のように修正します。

[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
flask = "2.3.1"

[dev-packages]

[requires]
python_version = "3.11"

この状態でライブラリを再インストール後、Trivyで再スキャンをかけます。

$trivy fs ./
2023-12-06T20:19:17.159+0900    INFO    Loaded trivy.yaml
2023-12-06T20:19:17.170+0900    INFO    Vulnerability scanning is enabled
2023-12-06T20:19:17.170+0900    INFO    Secret scanning is enabled
2023-12-06T20:19:17.170+0900    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-12-06T20:19:17.170+0900    INFO    Please see also https://aquasecurity.github.io/trivy/v0.48/docs/scanner/secret/#recommendation for faster secret detection
2023-12-06T20:19:17.177+0900    INFO    Number of language-specific files: 1
2023-12-06T20:19:17.177+0900    INFO    Detecting pipenv vulnerabilities...

Pipfile.lock (pipenv)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

┌─────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                           Title                            │
├─────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤
│ flask   │ CVE-2023-30861 │ HIGH     │ fixed  │ 2.3.1             │ 2.3.2, 2.2.5  │ flask: Possible disclosure of permanent session cookie due │
│         │                │          │        │                   │               │ to missing Vary: Cookie...                                 │
│         │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-30861                 │
└─────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘

脆弱性が検知されました。
出力されているURLから、想定していたものが検出されていることがわかります。
インストールされているライブラリに脆弱性が含まれていれば、Trivyはそれらを自動で検知可能で、脆弱性情報と結びつけることもできます。

終了コードの指定

Trivyのコマンドは、下記のようにすることで、脆弱性を検知した場合に終了コードを1にすることができます。
GitHub ActionsやAWS CodePipelineなどCI/CDは、プロセス内のコマンドが終了コードゼロ以外を返すと、プロセスを中断します。
従って、CI/CDに脆弱性検知を組み込み、検知したらデプロイしないようにすることも可能です。

trivy fs ./ --exit-code 1

aquasecurity.github.io

Trivyの解析結果をSBOM形式で出力する

Trivyのコマンドは、--formatオプションで何も指定しなければ、表形式となります。
この場合、脆弱性が見つからなければ何も出力されません。

Trivyの検査結果をソフトウェアコンポーネントの一覧としてのSBOM形式のファイルで出力するには、--formatオプションでcyclonedx,spdx,spdx-jsonなどを指定します。
(ちなみに、--formatオプションで指定可能な値は、公式ドキュメントに載っていないので、--helpで確認するのをお勧めします。)

SBOMの代表的なフォーマットであるSPDXを指定してみると、以下のような出力になります。
--formatオプションを指定しない時と異なり、脆弱性が見つからなくても出力されているのがわかります。
これが、ソフトウェアコンポーネントの一覧としてのSBOMです。
これをいつでも提示可能にすることが、SBOM用いたソフトウェアコンポーネント管理の目的の一つです。

$ trivy fs ./ --format spdx-json --output spdx-json-by-trivy.json

SBOM形式での出力結果はこちら
上部が脆弱性なしのバージョンのFlaskを使用した結果、下部が脆弱性ありのバージョンのFlaskを使用した結果です。
gist.github.com

SBOM形式で出力すれば、それを元に脆弱性検知を行うこともできます。
つまり、自前で一気に脆弱性検知をすることもできれば、作成したSBOMを渡して、他者に脆弱性検知を行ってもらうこともできます。

$ trivy fs ./ --format spdx-json --output spdx-json-by-trivy.json
$ trivy sbom spdx-json-by-trivy.json 
2023-12-10T02:48:02.894Z        INFO    Vulnerability scanning is enabled
2023-12-10T02:48:02.895Z        INFO    Detected SBOM format: spdx-json
2023-12-10T02:48:02.898Z        INFO    Number of language-specific files: 1
2023-12-10T02:48:02.899Z        INFO    Detecting pipenv vulnerabilities...

Pipfile.lock (pipenv)

Total: 1 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 1, CRITICAL: 0)

┌─────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability  │ Severity │ Status │ Installed Version │ Fixed Version │                           Title                            │
├─────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────────┤
│ flask   │ CVE-2023-30861 │ HIGH     │ fixed  │ 2.3.1             │ 2.3.2, 2.2.5  │ flask: Possible disclosure of permanent session cookie due │
│         │                │          │        │                   │               │ to missing Vary: Cookie...                                 │
│         │                │          │        │                   │               │ https://avd.aquasec.com/nvd/cve-2023-30861                 │
└─────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────────┘

GitHub ActionsでTrivyを使う

Trivyは、公式でGitHub Actionsがフォローされています。

github.com

プロジェクトフォルダに.github/workflowsフォルダを作成し、GitHub Actionsのワークフローを記述したYAMLファイルを作成します。

name: Trivy Vulnerability Scan

on:
  push:
    branches:
    - main
  pull_request:
jobs:
  build:
    name: Build
    runs-on: ubuntu-20.04
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Run Trivy vulnerability scanner in fs mode
      uses: aquasecurity/trivy-action@master
      with:
        scan-type: 'fs'
        scan-ref: '.'
        trivy-config: trivy-config.yaml

ファイルシステムを対象に、trivy-config.yamlを設定ファイルとして、脆弱性検知を実行します。
scan-ref: '.'なので、trivy-config.yamlはプロジェクト直下に置きます。

format: table
exit-code: 1
severity:
  - UNKNOWN
  - LOW
  - MEDIUM
  - HIGH
  - CRITICAL

exit-code: 1を指定して、脆弱性が見つかった場合はプロセスを中断するようにします。
この状態でFlaskを脆弱性があるバージョンにして、GitHub Actionsを実行します。
すると、GitHub Actionsが失敗し、脆弱性が見つかったことがわかります。

脆弱性を検知したことによるGitHub Actionsの失敗

これで、脆弱性が見つかった場合、GitHub Actionsのワークフローで脆弱性検知し、プロセスを中断することができることがわかりました。
このように、GitHub ActionsにTrivyを組み込むことで、CI/CDに脆弱性検知を導入して脆弱性のあるコードをデプロイしないようにすることができます。

GitHubのDependency graphとTrivy

GitHub Actionsの流れでDependency graphに触れさせてください。
Dependency graphは、GitHubの機能の一つです。
有効にすると、GitHubのリポジトリにあるソフトウェアコンポーネントの依存関係を可視化することができます。

docs.github.com

この機能を有効にすることで、ソフトウェアコンポーネントの管理と脆弱性検知を行うことができます。
Trivyによる解析と似た機能を持ちます。

現状、Dependency graphはデフォルトブランチがターゲットです。
デフォルトブランチがデフォルト設定のままなら、mainブランチがターゲットになります。 複数のブランチやプルリク時などをターゲットにする場合は、GitHub Actions+Trivyの方が便利そうです。

まとめ

今回は以上です。
SBOMの現状と、Trivyを使った脆弱性検知についてご紹介しました。
2023年12月現在、SBOMに対応したサービスも登場しつつありますが、基本的な動作を把握するためあえてTrivyを使ってみました。
使ってみたところ、よく悪くも便利すぎて一気に脆弱性検知ができてしまうので、肝心のSBOMが見ることができないという状況になりましたので、ゆっくりと触ってみたよかったと思います。
Trivyとtrivy-actionの動きが把握できたことで、CI/CDパイプラインに脆弱性管理を組み込み、安全なソフトウェアを提供する道筋が見えてきました。

SBOMに関してまた新しい情報が入りましたら、また記事にしたいと思います。


サーバーワークスでは、開発プロセスの最適化を支援するサービスを提供しています。

www.serverworks.co.jp

www.serverworks.co.jp

兼安 聡(執筆記事の一覧)

アプリケーションサービス部 DS3課所属
AWS認定全冠、認定スクラムマスター
広島在住です。今日も明日も修行中です。