この記事は、 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社によるレビューやパッケージシステムとの対応づけが完了しており、影響を受けるバージョンや修復済みのバージョンの情報も含まれているため、対応策定に役立てることができます。
本ブログ執筆時点で対応しているエコシステムは以下の通りです。
- Composer (registry: https://packagist.org/)
- Erlang (registry: https://hex.pm/)
- Go (registry: https://pkg.go.dev/)
- GitHub Actions (https://github.com/marketplace?type=actions/)
- Maven (registry: https://repo.maven.apache.org/maven2)
- npm (registry: https://www.npmjs.com/)
- NuGet (registry: https://www.nuget.org/)
- pip (registry: https://pypi.org/)
- pub (registry: https://pub.dev/packages/registry)
- RubyGems (registry: https://rubygems.org/)
- Rust (registry: https://crates.io/)
使い方
Dependabot を利用するには、Repositoryページの Settings
-> Code security and analysis
にアクセスし、 Dependency graph
および Dependabot
関連の機能をEnableにする必要があります。
GitHub Dependency GraphはRepositoryの中の言語依存ライブラリの管理ファイルなどを解析し、依存しているライブラリの一覧を抽出する機能です。 Dependency Graph で解析できる対象はAbout the dependency graph のドキュメントを参照してください。
Dependency Graphを有効にするとソースコードが解析され、依存パッケージが確認できる様になります。
解析結果はRepositoryページの Insight
→ Dependency 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-type
や scan-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.yaml
のseverity
を用いると、スキャンの対象とする脆弱性の脅威レベルを設定できます。
上記の例ではTrivy基準でHigh以上の脆弱性を検知すると、exit-code: 1
の設定によりActionがfailし、脆弱性を発見できる仕組みになっています。
Pipenvの採用
上記の仕組みを実現するために、Pythonのパッケージマネージャーとして pipenv を採用しています。
Pythonで環境を構築するには、requirements.txt
にインストールしたいパッケージを記述しておき、pipでインストール方法もあります。
pip install -r requirements.txt
しかし私たちは以下の理由でpipenvを採用しています。
- 依存するパッケージを
Pipfile.lock
で明確化してTrivyでスキャンできる - 開発環境でしか利用しないパッケージを分離し、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アカウント にて情報発信していく予定です。このアカウントでは セキュリティインテリジェンスの発信 などの活動も行っているので、ぜひフォローしてください。
それでは、明日もお楽しみに。
- https://go.snyk.io/rs/677-THP-415/images/State%20Of%20Open%20Source%20Security%20Report%202020.pdf↩
- https://github.co.jp/pricing.html↩
-
pip freeze
を利用してインストールされているパッケージを全て出力することでfsスキャンによる検知が可能になりますが、直接依存するパッケージと間接的に依存するパッケージが混在してしまいます。↩