東京スタジオでサーバーエンジニアをしている有澤です。
私が担当しているプロジェクトにおいて、セキュリティ対策に関する取り組みの一環として、脆弱性検知の仕組みを導入する機会がありました。
様々なサービスやツールを比較・検討した結果、TrivyとAWS Security Hubをベースにした構成でシステム構築を行う形になりました。
本記事では、上記のような技術選定を行った理由や具体的なアーキテクチャを紹介し、実際に運用してみた感想についても触れていこうと思います。
背景
近年、サーバーサイドで広く普及しているフレームワークやライブラリで、クリティカルな脆弱性が相次いで見つかっています。
2021年12月には、Javaのログ出力ライブラリであるApache Log4jにおいて、リモートコード実行(RCE)の脆弱性(Log4Shell)が発見されました。この脆弱性の特徴の一つとして、攻撃が比較的容易に成立する点が挙げられ、2022年7月現在においても情報流出などの被害が発生し続けています。
2022年3月には、JavaのWebアプリケーションフレームワークであるSpring Frameworkにおいて、Log4Shellと同様のRCE脆弱性(Spring4Shell)が発見されました。Log4Shellと比較すると、攻撃の成立条件が厳しいため被害状況としては限定的ですが、この脆弱性を利用したマルウェアの存在が確認されており、特にシンガポール地域において感染被害が報告されています。
弊社のサービスでは主にPHPをサーバーサイドで使用していますが、国内外で近年勢いのあるPHPフレームワークのLaravelに関しても、2021年1月にRCE脆弱性(CVE-2021-3129)が見つかっています1。そして、この脆弱性を利用したマルウェアに感染し、仮想通貨マイニングの被害を受けた事例も報告されています。
仮想通貨のマイニング程度であれば被害は限定的ですが、ソースコードの流出やデータベースの改竄、認証情報の悪用などにより、サービスの継続が困難な状況に陥ったり、会社の信用が失墜する可能性も否定できません。
このように、事業上のリスクを抑える観点で脆弱性対策はとても重要なのですが、サーバーアプリケーションが依存するライブラリやOSのパッケージは数多く存在し、それら全ての脆弱性情報を継続的にキャッチアップするのは困難です。
そこで、システムに含まれる脆弱性を自動で検知し、対応状況を管理するための仕組みを導入することにしました。
実現したいこと
今回、脆弱性検知の仕組みを導入するにあたって、チーム内で検討を行い、以下のような方向性で進めることにしました。
- 検査対象のレイヤーとして、OS管理のパッケージだけではなく、アプリケーションが依存するフレームワークやライブラリを含める
- 先述の「背景」で例として挙げた脆弱性は、全てアプリケーションレイヤーのものであり、これらのリスクを許容できないと判断したため
- 検査をCI/CDパイプラインには組み込まず、独立して定期実行する
- プロジェクトの性質上、脆弱性対応を行うタイミングを調整する必要があるため
- 脆弱性の対応状況をWeb画面上で管理する
- プロジェクトの性質上、脆弱性対応を行うタイミングを調整する必要があるため
- 新規で脆弱性が検出された場合は、その脆弱性の概要と、詳細ページへのリンクをSlackに通知する
- チーム内でのコミュニケーションをSlack上で主に行っており、確認する機会が多いため
- 工数や費用はなるべく抑える
- 全社的に導入するわけではなく、あくまでチーム内で試しに導入してみるといった温度感のため
脆弱性検査ツールの調査
担当プロジェクトでは、サーバーアプリケーションのコンテナイメージをAmazon ECRに保存し、ECS/Fargateで実行する構成でした。
そこで、ECR上のコンテナイメージ、あるいはECS/Fargate上のアプリケーションに対して、脆弱性検査を行うためのツールの調査を行いました。
その中で候補として挙がったツールは下記の通りです。
Vuls
Linux/FreeBSD向けの、エージェントレスな脆弱性検査ツールです(Github)。フューチャーアーキテクト株式会社が開発し、2016年にOSSとして公開されました。
主な特徴としては、下記の3点が挙げられます。
- 検査対象のサーバーに対して、3種類の検査モード(リモートスキャン、ローカルスキャン、サーバー)に対応
- OS管理のパッケージに加えて、プログラミング言語のライブラリや、自分でコンパイルしたミドルウェアの脆弱性検査に対応
- OSS版とともにクラウドサービス版(FutureVuls)が存在し、脆弱性管理の機能が充実している
Amazon ECRのイメージスキャン機能
Amazon ECRの機能の一つとして、保存されているコンテナイメージの脆弱性検査を行う機能が存在します。ベーシックスキャンと拡張スキャンの2つのタイプが存在します。
ベーシックスキャンの特徴は下記の通りです。
- OS管理のパッケージの脆弱性検査のみ対応
- AWS Security Hubとの連携に非対応
- 任意のタイミングで脆弱性検査が実行可能(手動スキャン)
- 無料
拡張スキャンの特徴は下記の通りです2。
- OS管理のパッケージに加えて、プログラミング言語のライブラリの脆弱性検査に対応
- AWS Security Hubとの連携により、脆弱性管理が可能
- イメージのPush時の自動スキャンが必須(手動スキャンは非対応)
- 有料(スキャン回数に応じて、費用が発生)
レジストリ全体にスキャン設定が適用されるため、上記2つのスキャンタイプを同一レジストリ(AWSアカウント)で併用することはできません。また、スキャン対象に含めるかは、リポジトリ単位で選択します。
Trivy
シンプルで包括的なセキュリティスキャナーです(公式サイト)。2019年にTeppei Fukuda氏が個人でOSSとして開発し、その後、Aqua Security社に譲渡されました。
主な特徴としては、下記の点が挙げられます。
- コンテナイメージやファイルシステム、Gitリポジトリなど、様々な対象に対して、脆弱性検査が可能
- Amazon ECR等のプライベートレジストリにも対応
- OS管理のパッケージに加えて、プログラミング言語のライブラリの脆弱性検査に対応
- AWS Security Hubとの連携により、脆弱性管理が可能
- 脆弱性検査の他に、IaCに関するファイル(TerraformやDockerfile)の設定検証や、認証情報がソースコード内に埋め込まれていないかのチェックも可能
脆弱性検査ツールの選定
ツールの選定を行うにあたって、評価項目をいくつか設定し、比較を行った結果が次の表の通りとなります。
Vuls(FutureVuls) | ECR (ベーシック) | ECR (拡張スキャン) / Security Hub |
Trivy / Security Hub |
|
---|---|---|---|---|
必須要件を満たしているか | ○ | × | ○ | ○ |
導入の容易さ | △ | ◎ | ◎ | ○ |
運用コスト | △ | ○ | △ | ○ |
機能の豊富さ | ◎ | △ | △ | ○ |
Trivyを使用した構成が、全体的にバランスが良かったため、今回はそちらを採用することにしました。
Vuls(FutureVuls)については、脆弱性の管理機能が充実していることから、組織全体で導入するようなケースで適していると感じました。
また、ECRのイメージスキャン機能に関しては、導入が特に容易なため、スピード感を重視する場合に適していると感じました。
評価項目ごとの詳細は、下記の通りです。
必須要件を満たしているか
「実現したいこと」で挙げた項目を満たしているかの評価です。ECR(ベーシック)に関して、「検査対象にアプリケーションが依存するフレームワークやライブラリを含める」と「脆弱性の対応状況をWeb画面上で管理する」の要件を満たすことが難しいため、×としました。その他のツールは全ての要件を満たしているため、○としています。
導入の容易さ
ECRのイメージスキャン機能に関して、AWSコンソール上でボタンを数クリックするだけで導入可能なため、◎としました。Trivyについては、インストールやスキャン実行が容易で、AWSとの連携も整備されているため、○としています。Vuls(FutureVuls)は機能が豊富であることから、その把握やアカウントのセットアップにある程度時間が掛かることが予想されるため、△としています。
運用コスト
Trivyに関しては、運用時のコストを試算したところ、無視できるレベルの金額だったので、○としています。ECR(拡張スキャン)については、イメージのPush時の自動スキャンが必須であり、またスキャン対象のリポジトリが複数存在する場合はその分、スキャンが実行されて費用が発生することから、イメージの更新頻度が高い場合はある程度の金額となるため、△としています。
機能の豊富さ
Vuls(FutureVuls)では多様な機能が提供されていることから、◎としました。それに対して、ECRのイメージスキャン機能では制約が大きく、検査のタイミングや範囲の調整が難しいことから、△としています。
システム構成
今回、構築した脆弱性検知システムの構成は上図の通りです。
ECS Scheduled TaskでTrivyのイメージスキャンを実行し、検出結果をSecurity Hubに登録します。その後、Slack通知をした上で、Security Hubに登録された脆弱性情報のステータス変更を、AWS Lambdaにより行っています。
それぞれの構成要素について、以下でより詳しく見ていきます。
ECS Scheduled Taskによるイメージスキャンの実行
ECSでタスクを定期実行するための仕組みとして、ECS Scheduled Taskという機能が存在します。ECS Scheduled Taskの設定を行うと、Amazon EventBridgeルールが作成され、指定したスケジュールに沿ってECSタスクが実行されます。
ECSタスクで実行するコンテナについては、Trivyの公式ドキュメントを参考に、下記のようなDockerfile、及びスクリプトを使用します。
Dockerfile
FROM amazon/aws-cli:2.7.19
RUN yum install -y tar gzip
RUN curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/v0.30.4/contrib/install.sh \
| sh -s -- -b /usr/local/bin
RUN curl -sfLO https://raw.githubusercontent.com/aquasecurity/trivy/v0.30.4/contrib/asff.tpl
COPY ./entrypoint.sh .
ENTRYPOINT ["./entrypoint.sh"]
.env
AWS_REGION=ap-northeast-1
AWS_ACCOUNT_ID=123456789012
IMAGE_URL=xxx.dkr.ecr.ap-northeast-1.amazonaws.com/yyy
entrypoint.sh
#!/bin/bash
# Trivyによる脆弱性検査の実行
trivy image --format template --template "@asff.tpl" -o report.asff --security-checks vuln $IMAGE_URL
# 検出結果をSecurity Hubへ送信
aws securityhub batch-import-findings --cli-input-json file://report.asff
スクリプト内の処理としては、Trivyでイメージスキャンを実行し、検出結果をAWS Security Finding Format(ASFF)という形式で保存して、Security Hubへ送信しています。
注意点として、Security Hubへ検出結果を送信する前に、Security Hubの設定画面で「Aqua Security: Aqua Security」との統合を有効化しておく必要があります3。
EventBridgeイベントをトリガーとしたSlack通知
Amazon EventBridgeは、複数のAWSサービスの連携を主な役割とするサービスです。公式ドキュメントの説明を以下で引用します。
EventBridgeは、環境の変化の指標であるイベントを受信し、イベントをターゲットにルーティングするルールを適用します。ルールは、イベントパターンと呼ばれるイベントの構造、またはスケジュールのいずれかに基づいて、イベントをターゲットにマッチングさせます。例えば、Amazon EC2インスタンスが保留から実行に変わったときにイベントをLambda関数に送るルールを設定することができます。
Security Hubに検出結果が追加、あるいは更新された際に、「Security Hub Findings - Imported」というタイプのEventBridgeイベントが発行されます。
そこで、以下のようなイベントパターンをトリガーとするEventBridgeルールを作成することで、Security Hubへ脆弱性が送られた際に、ターゲットを実行することが可能です。
{
"source": ["aws.securityhub"],
"detail-type": ["Security Hub Findings - Imported"],
"detail": {
"findings": {
"GeneratorId": [{"prefix": "Trivy/" }],
"Workflow": {
"Status": ["NEW"]
}
}
}
}
加えて、後述するSecurity Hubの脆弱性情報のステータス変更を行うLambdaと組み合わせることで、新規検出時にのみターゲットを実行することが可能となります。
AWSの各種サービスと、Slackなどのチャットサービスとを連携させるためのサービスとして、AWS Chatbotが存在します。EventBridgeルールのターゲットとして、AWS Chatbotが紐付いたSNSトピックを指定することで、脆弱性検知時に下図のようなSlack通知が可能です。
Security Hubによる脆弱性情報の管理
Security Hubでは、検出された脆弱性のステータスを「NEW / NOTIFIED / SUPPRESSED / RESOLVED」の4つの状態で管理します。
新規で検出された脆弱性はNEWのステータスで登録されます。
NEWからNOTIFIEDへの変更は、Slack通知後にLambdaで自動的に行います。以下のようなスクリプトで実現できます。
import boto3
client = boto3.client("securityhub")
def lambda_handler(event, context):
for finding in event['detail']['findings']:
finding_id = finding['Id']
product_arn = finding['ProductArn']
client.batch_update_findings(
FindingIdentifiers=[{"Id": finding_id, "ProductArn": product_arn}],
Workflow={"Status": "NOTIFIED"}
)
NOTIFIEDからRESOLVED、あるいはSUPPRESSEDへの変更は、手動で行います。脆弱性検知システムの利用者の対応フローとしては下図のような流れとなります。
脆弱性の確認を行い、修正した場合はRESOLVEDへ、対応不要と判断した場合はSUPPRESSEDへ変更します。
実際に使用してみた感想
今回、構築した脆弱性検知システムを使い始めて、3ヶ月ほどになります。担当プロジェクトにおいて必要十分な機能を実現できていると、個人的には感じています。
脆弱性情報を確認/対応する習慣が身に付いた
元々、ECRのイメージスキャン(ベーシック)を有効にしていたのですが、通知設定は行っていなかったため、検査結果はあまり確認していませんでした。今回構築した脆弱性検知システムでは、Slack通知により脆弱性情報がすぐに目に入るようになっており、自然と確認できる環境が出来ました。
また、検出結果に含まれるリンクを押すと、Aqua Security社が提供している脆弱性データベースの詳細情報を確認できるのですが、複数の情報源の内容が分かりやすくまとまっています(下図はSpring4Shellの例です)。
このように、脆弱性対応のフローが整ったことで、継続的な対応が行えるようになりました。
検査対象の調整は一部必要だった
運用を始めた当初は、通知量が多くて確認が負担に感じることがありました。そこで検査対象の調整を2点ほど行いました。
一つ目は検出対象とする重大度のレベルです。Trivyにおいて、検出された脆弱性は「LOW / MEDIUM / HIGH / CRITICAL」の4種類の重大度に分類されます。検出対象とする重大度はイメージスキャン実行時のオプションで設定可能なため、そちらの調整を行いました。
二つ目は複数のリポジトリを検査対象とした際に、同一の脆弱性が重複して検出される問題についてです。同じOSを使用するコンテナが複数存在する場合に特に多く発生します。それぞれのコンテナイメージに含まれる脆弱性を正確に把握するという観点では正しい挙動だとは思うのですが、確認時に重複分を省く必要があり、やや手間が掛かります。検出結果をSecurity Hubへ送信する際の設定を調整することで、コンテナが異なっていたとしても脆弱性IDが同じであれば同一の脆弱性として扱うことが可能だったので、そのような対応を行いました。
これらの調整を行ったことで、よりスムーズに脆弱性の確認作業を進められるようになりました。
ランタイムやフレームワークの継続的なアップデートに繋がる可能性も
話が少し変わりますが、サービスが運用フェーズに入ると、ランタイムやフレームワークなどの、影響範囲が大きいソフトウェアのアップデートは難しくなります。その一因として、コード修正や検証作業の工数確保に関する問題が挙げられます。
モバイルアプリのサービスの場合、クライアントサイドについてはプラットフォーム側(App Store / Google Play Store)からの要求に関連して、ゲームエンジンやライブラリのアップデートが必要となる場面があるのですが、サーバーサイドでは特にそういった機会はありません。セキュリティサポートの期限を過ぎたとしても、これまで通り機能はするので、そのままのバージョンで使い続けることもあるかと思います。
一方で、エンジニアの中には新機能を使いたい人も多いのではないでしょうか。PHPを例に挙げると、ここ数年で厳密な書き方4や、モダンな書き方5をするための機能が数多く追加されており、コーディングの際に感じる印象も変わってきています。
脆弱性情報を把握し、セキュリティリスクを正しく評価できるようになっておくことで、ランタイムやフレームワークの継続的なアップデートを提案しやすくなるかもしれないと感じました。
まとめ
今回の記事では、脆弱性検知システムの導入事例についてご紹介しました。どのようなプロジェクト・組織に対して導入するかによって、最適な技術構成は異なります。各ツールの特徴を把握して、状況に応じて適切な選定を行っていきたいですね。
また、TrivyとAWS Security Hubを使用した場合の具体的な構築方法についても取り上げました。基本的には公式ドキュメントの説明通りに進める形で問題ないですが、一部調整が必要な点はありました。同様の構成で導入を検討されている方の参考になれば幸いです。
-
厳密にはLaravel自体の脆弱性ではなく、Laravelが依存している、エラー画面生成用のライブラリ(Ignition)の脆弱性です。 ↩
-
2021年11月にAmazon Inspectorの新バージョンがリリースされ、Amazon ECRの拡張スキャンに関しても機能追加が行われました。 ↩
-
有効化していない場合、BatchImportFindingsのAPI実行時にAccessDeniedExceptionが発生します。 ↩