ネットワークサービス部の田上(id:rtagami)です。 オーディオインタフェースを物色していたのですが、ここに来て YAMAHAのDM3 がリリースされてしまったので、もはやこれを買うしか無いのではないかという気持ちになっています。
この記事では、オープンソースライブラリの脆弱性とどう向き合っていくかという観点から、 脆弱性スキャナであるTrivyをご紹介した上で、 わたしたちがTrivyをどう活用しているかをご紹介します。
なお、前回の記事は 【CI戦術編 その10】 Renovateで依存ライブラリのアップデートに負けない方法 - FJCT Tech blog でした。
オープンソースライブラリと脆弱性
現代のソフトウェア開発において、 オープンソースのライブラリを一切使用せずにプロダクトを作り上げることは困難でしょう。 オープンソースのライブラリを活用することによって、 プロダクトの本質的な価値を高める活動に時間を割けるようなるからです。
オープンソースのライブラリを使う上での課題の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は欲しい機能と導入の手間のバランスが良かったので導入しましたが、 これだけで脆弱性対策が出来ているということではありません。 その他の脆弱性対策も必要に応じて導入することが必要であり、 どの程度の対策が必要であり、かつ適切なのかをチームで決定し、 常に見直していく必要があるものであることには留意する必要があります。
次回は、これまでご紹介したCIの仕掛けを振り返りながら、 全体としてわたしたちのCIの全体像をご紹介します。 お楽しみに。
連載バックナンバー
CI戦術編
- 【CI戦術編 その1】alembic check コマンドを活用したマイグレーションスクリプト生成忘れ防止
- 【CI戦術編 その2】コードレビューを充実したものにする方法、あるいは一生残る恥ずかしい履歴を作らないように
- 【CI戦術編 その3】Pythonのimportのことならまかせろ isort
- 【CI戦術編 その4】Blackを利用したコーディングスタイルの統一
- 【CI戦術編 その5】Pythonで明示的に型を書く理由
- 【CI戦術編 その6】Python開発の強い味方 Pylint
- 【CI戦術編 その7】 pyupgradeを使って最新の記法に対応してみた
- 【CI戦術編 その8】OAS(OpenAPI Specification)で仕様書を自動生成しよう
- 【CI戦術編 その9】自動生成しか勝たん openapi-typescript
- 【CI戦術編 その10】 Renovateで依存ライブラリのアップデートに負けない方法
- 【CI戦術編 その11】 Trivy: あなたの使ってるライブラリ、大丈夫ですか?