FJCT Tech blog

富士通クラウドテクノロジーズ公式エンジニアブログです

富士通クラウドテクノロジーズ

FJCT/Tech blog

【CI戦術編 その11】Trivy: あなたの使ってるライブラリ、大丈夫ですか?

ネットワークサービス部の田上(id:rtagami)です。 オーディオインタフェースを物色していたのですが、ここに来て YAMAHAのDM3 がリリースされてしまったので、もはやこれを買うしか無いのではないかという気持ちになっています。

この記事では、オープンソースライブラリの脆弱性とどう向き合っていくかという観点から、 脆弱性スキャナであるTrivyをご紹介した上で、 わたしたちがTrivyをどう活用しているかをご紹介します。

なお、前回の記事は 【CI戦術編 その10】 Renovateで依存ライブラリのアップデートに負けない方法 - FJCT Tech blog でした。

tech.fjct.fujitsu.com

オープンソースライブラリと脆弱性

現代のソフトウェア開発において、 オープンソースのライブラリを一切使用せずにプロダクトを作り上げることは困難でしょう。 オープンソースのライブラリを活用することによって、 プロダクトの本質的な価値を高める活動に時間を割けるようなるからです。

オープンソースのライブラリを使う上での課題の1つが脆弱性の管理です。 前回の記事でご紹介したRenovateなどを使えば、 プロダクトか直接依存しているライブラリをバージョンアップし続けることは比較的簡単です。 しかし、プロダクトが直接的に依存していない(間接的に依存している)ライブラリのアップデートは Renovateの得意とするところではありません。 また、Renovateなどで常に最新のライブラリを使うように心がけていたとしても、 最新のバージョンのライブラリに脆弱性があることもあります。

Apache Log4jの脆弱性で業界内が大騒ぎになったことは記憶に新しいですが、 Pythonでもctxパッケージの乗っ取り によって認証に用いられるトークンが盗まれ外部に送信されるという事件がありました。

脆弱性のないライブラリしか使わなければ大丈夫、と言いたいところですが、 脆弱性のあるライブラリがプロダクトに使われることを防ぐのは現実的には不可能です。 例えばApache Log4jの場合は、 脆弱性のあるコードがリリースされてからその事実が発覚するまでに年単位の時間が経過しています。 現実的には、プロダクトが使っているライブラリに脆弱性がある可能性を受け入れた上で、 脆弱性とうまく付き合っていくことが求められます。

継続的な脆弱性対策と自動化の必要性

脆弱性とうまく付き合っていくためには日頃からの情報収集が欠かせません。 例えばプロダクトが利用しているライブラリの一覧を作成し、 毎日各種の脆弱性情報サイトを巡回して一覧と突き合わせるというような対策が考えられますが、 これはなかなか困難な対応になると言わざるをえないでしょう。 2022年に登録されたCVEの数は25000を超えており、毎日70以上のCVEが登録されています。 わたしたちのプロダクトはバックエンドだけで120以上のライブラリに依存しており、 それぞれのCVEに対して120個を超えるライブラリが該当しているかを確認する必要があることになります。 とても人間の能力では対応できませんし、人間でなくてもどうにかできる気はします。

Trivyは、 このような需要に対応する脆弱性スキャナです。

Trivy導入のきっかけと選定理由

Trivyはセキュリティに関するスイスアーミーナイフのようなもので、 環境と需要に合せてさまざまな使い方ができる脆弱性スキャナですが、 本記事ではプロダクトの依存ライブラリの脆弱性を見つけ出すことに限定してご紹介します。

わたしたちのプロダクトでもPython ctxの問題をきっかけに、 そろそろ脆弱性スキャナを入れておかないとマズいのでは、 という事になり、脆弱性スキャナとしてTrivyを導入しました。 わたしたちが脆弱性スキャナを導入する上でTrivyを採用した理由は以下のとおりです。

導入が簡単

TrivyはGoで書かれており、それ故の特徴としてシングルバイナリであることが挙げられます。 とりあえずバイナリをダウンロードしてくれば実行できてしまうというお手軽さは何物にも代えられません。 また、公式のコンテナイメージも提供されており、コンテナ環境下ではワンライナーで実行できます。

脆弱性情報データベースを自動的にダウンロードしてくれる

Trivyは実行時に最新の 脆弱性情報データベース を勝手にダウンロードしてくれます。 データベースのダウンロードにユーザー登録の必要がなく、 自前でデータベースの作成をする必要もないというのは、 細かいことではありますが使い勝手の良さを感じるところです。

実行結果をJSONで取得できる

これまた細かいことですが、Trivyは実行結果をJSONとして出力できます (人間が見るためのアスキーアート表形式でも出力できます)。 JSONで出てきた結果をパイプでスクリプトに渡せばあらゆる後処理が可能です。

Trivyの使い方

先述のように、Trivyの導入と実行はとても簡単です。 今回はビルド済みバイナリをダウンロードして /usr/local/bin に置く形でインストールします。

$ curl -LO https://github.com/aquasecurity/trivy/releases/download/v0.40.0/trivy_0.40.0_Linux-64bit.tar.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 48.0M  100 48.0M    0     0  19.0M      0  0:00:02  0:00:02 --:--:-- 28.6M
$ tar zxfv trivy_0.40.0_Linux-64bit.tar.gz
LICENSE
README.md
contrib/asff.tpl
contrib/gitlab-codequality.tpl
contrib/gitlab.tpl
contrib/html.tpl
contrib/junit.tpl
trivy
$ sudo cp trivy /usr/local/bin
$ trivy --version
Version: 0.40.0

trivy バイナリにバスが通ったら、あとは実行するだけです。 一旦mainブランチに対して実行してみます。

$ trivy fs ~/path/to/your/repository
2023-04-24T18:29:45.062+0900    INFO    Need to update DB
2023-04-24T18:29:45.062+0900    INFO    DB Repository: ghcr.io/aquasecurity/trivy-db
2023-04-24T18:29:45.062+0900    INFO    Downloading DB...
36.50 MiB / 36.50 MiB [----------------------------------------------------------------------------------------------------------------] 100.00% 10.27 MiB p/s 3.8s
2023-04-24T18:29:50.032+0900    INFO    Vulnerability scanning is enabled
2023-04-24T18:29:50.032+0900    INFO    Secret scanning is enabled
2023-04-24T18:29:50.032+0900    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-04-24T18:29:50.032+0900    INFO    Please see also https://aquasecurity.github.io/trivy/v0.40/docs/secret/scanning/#recommendation for faster secret detection
2023-04-24T18:29:54.753+0900    INFO    Number of language-specific files: 4
2023-04-24T18:29:54.754+0900    INFO    Detecting poetry vulnerabilities...
2023-04-24T18:29:54.758+0900    INFO    Detecting yarn vulnerabilities...
$

脆弱性情報データベースのダウンロードと合せて10秒ほどで終わりました。 何も言われなかったので特に問題はなさそうです。

…と、ここで終わってしまっては記事になりませんので、 試しにリポジトリを2023年の年明け頃に巻き戻して再度実行してみましょう。

$ trivy fs ~/path/to/your/repository
2023-04-24T18:41:42.551+0900    INFO    Vulnerability scanning is enabled
2023-04-24T18:41:42.551+0900    INFO    Secret scanning is enabled
2023-04-24T18:41:42.551+0900    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-04-24T18:41:42.551+0900    INFO    Please see also https://aquasecurity.github.io/trivy/v0.40/docs/secret/scanning/#recommendation for faster secret detection
2023-04-24T18:41:47.247+0900    INFO    Number of language-specific files: 4
2023-04-24T18:41:47.247+0900    INFO    Detecting poetry vulnerabilities...
2023-04-24T18:41:47.252+0900    INFO    Detecting yarn vulnerabilities...

backend/poetry.lock (poetry)

Total: 4 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 2, CRITICAL: 0)

┌──────────────┬─────────────────────┬──────────┬───────────────────┬───────────────┬──────────────────────────────────────────────────────────────┐
│   Library    │    Vulnerability    │ Severity │ Installed Version │ Fixed Version │                            Title                             │
├──────────────┼─────────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ cryptography │ CVE-2023-0286       │ HIGH     │ 38.0.4            │ 39.0.1        │ X.400 address type confusion in X.509 GeneralName            │
│              │                     │          │                   │               │ https://avd.aquasec.com/nvd/cve-2023-0286                    │
│              ├─────────────────────┼──────────┤                   │               ├──────────────────────────────────────────────────────────────┤
│              │ CVE-2023-23931      │ MEDIUM   │                   │               │ python-cryptography: memory corruption via immutable objects │
│              │                     │          │                   │               │ https://avd.aquasec.com/nvd/cve-2023-23931                   │
├──────────────┼─────────────────────┼──────────┼───────────────────┼───────────────┼──────────────────────────────────────────────────────────────┤
│ starlette    │ CVE-2023-30798      │ HIGH     │ 0.22.0            │ 0.25.0        │ Starlette allows an unauthenticated and remote attacker to   │
│              │                     │          │                   │               │ specify any number of...                                     │
│              │                     │          │                   │               │ https://avd.aquasec.com/nvd/cve-2023-30798                   │
│              ├─────────────────────┼──────────┤                   │               ├──────────────────────────────────────────────────────────────┤
│              │ GHSA-74m5-2c7w-9w3x │ MEDIUM   │                   │               │ MultipartParser denial of service with too many fields or    │
│              │                     │          │                   │               │ files                                                        │
│              │                     │          │                   │               │ https://github.com/advisories/GHSA-74m5-2c7w-9w3x            │
└──────────────┴─────────────────────┴──────────┴───────────────────┴───────────────┴──────────────────────────────────────────────────────────────┘

無事に(?)脆弱性が見つかりました。

わたしたちが使っているライブラリの過去のバージョンには脆弱性があったようです (既に脆弱性のないバージョンにアップデートしているのでmainブランチでは報告されませんでした)。

Slackに通知する

前述のコマンドラインをそのままCIジョブに組み込んで、 脆弱性が見つかった場合には終了コードを非ゼロに すれば、簡易的には対応できたことになりそうです。

しかし、ここまで強固な対策を取ってしまうと、実用的な運用をすることが困難になります。 使用しているライブラリに1つでも脆弱性が発見されると、 それを修正するまでは一切のコミットをマージできなくなるためです。 現実的には以下のような状況が発生しがちであり、 個別の脆弱性に対して人間が判断をした上で対応する必要があります。

対策版がリリースされていない

脆弱性の公表と対策版のリリースの時系列は、場合によりけりです。 影響が甚大な、いわゆる"embargo"期間が設定される脆弱性の場合は、 脆弱性の公表と同時に対策版もリリースされることが多いため比較的スムーズに対応できますが、 通常の脆弱性ではそのようなものが設定されることはまずありません。 脆弱性が公表されてから対策版がリリースされるまでしばらく座して待つ事になります。

攻撃が成立しない

あるライブラリに脆弱性があるとされたとして、 その脆弱性を使った攻撃が明らかに成立しないようなライブラリの使い方しかしていない、 ということも比較的多くあります。 そのような場合には、検知された脆弱性を無視するという対応をとる事も考える必要があります。


上記のような理由から、 わたしたちのプロダクトでは通常のテストパイプラインではCIジョブを失敗させる事はせず、 nightlyパイプラインだけでTrivyを実行した上で結果をSlackに通知することとしました。 ここで役立つのがTrivyのJSON出力です。

先ほどの2023年の年明け頃のリポジトリに対して --format json オプション付きでTrivyを実行します。

$ trivy fs --format json ~/path/to/your/repository | head -n 10
2023-04-24T20:17:24.238+0900    INFO    Vulnerability scanning is enabled
2023-04-24T20:17:24.238+0900    INFO    Secret scanning is enabled
2023-04-24T20:17:24.238+0900    INFO    If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2023-04-24T20:17:24.238+0900    INFO    Please see also https://aquasecurity.github.io/trivy/v0.40/docs/secret/scanning/#recommendation for faster secret detection
2023-04-24T20:17:28.934+0900    INFO    Number of language-specific files: 4
2023-04-24T20:17:28.934+0900    INFO    Detecting poetry vulnerabilities...
2023-04-24T20:17:28.939+0900    INFO    Detecting yarn vulnerabilities...
{
  "SchemaVersion": 2,
  "ArtifactName": "/home/rtagami/path/to/your/repository",
  "ArtifactType": "filesystem",
  "Metadata": {
    "ImageConfig": {
      "architecture": "",
      "created": "0001-01-01T00:00:00Z",
      "os": "",
      "rootfs": {

長くなるのでJSONの続きは省略しますが、 さきほどのアスキーアート表形式で出力されていた情報がJSONとして標準出力に出てきます。

わたしたちは、このJSONを簡単なPythonスクリプトに渡してSlack Block Kitに変換した上で、 チームのSlackチャンネルに通知しています。 ちょうど直近でそのような状況があったので、 Slackのスクリーンショットを撮っておきました(画像は加工をしています)。

Trivyがお知らせをしている様子のスクリーンショット画像

この場合は、単純にバージョンを上げるだけで丸く収まる状況でしたので、 ささっとバージョンを上げるコミットをマージして無事に対策が完了しました。

まとめ

今回の記事では、Trivyを使ってオープンソースライブラリの脆弱性とうまく付き合っていく方法をご紹介しました。

ソフトウェアを運用するということは終わりのない脆弱性対策と真摯に向き合うことでもあり、 脆弱性をひとつひとつ潰しながらも継続的にソフトウェアの価値を高められる体制を作るということでもあります。

完璧な脆弱性対策というものは存在しません。 わたしたちから見てTrivyは欲しい機能と導入の手間のバランスが良かったので導入しましたが、 これだけで脆弱性対策が出来ているということではありません。 その他の脆弱性対策も必要に応じて導入することが必要であり、 どの程度の対策が必要であり、かつ適切なのかをチームで決定し、 常に見直していく必要があるものであることには留意する必要があります。

次回は、これまでご紹介したCIの仕掛けを振り返りながら、 全体としてわたしたちのCIの全体像をご紹介します。 お楽しみに。

連載バックナンバー