はじめに
Amazon S3 などのクラウドストレージは、画像、動画、ドキュメントなど、様々なファイルを格納するために広く利用されています。
しかし、クラウドストレージに対してマルウェアファイルをアップロードされるリスクも存在します。
特に、不特定多数のユーザーがファイルをアップロードするような環境では、下記のようなリスクが存在します。
- アップロードされたファイルにマルウェアが含まれている可能性がある
- システム間でファイルを共有する場合、保存されたマルウェアがシステム全体に拡大してしまう可能性がある
- ファイルをダウンロードしたユーザーの端末がマルウェアに感染する可能性がある
この記事では、Amazon GuardDuty Malware Protection for S3 を活用して、アップロードされたファイルを自動的にスキャンを行い、スキャンの結果に基づいてファイルを隔離する仕組みについてご紹介します。
GuardDuty Malware Protection for S3 について
GuardDuty Malware Protection for S3 は、S3 バケットにアップロードされたオブジェクトを自動的にスキャンすることができます。
スキャンしたオブジェクトにはタグが付与され、タグの値によって脅威と判定されたか判断できます。
なお、現状スキャンのタイミングはアップロード時のみで、定期的なスキャンなど、任意のタイミングでのスキャンには対応していません。
GuardDuty Malware Protection for S3 の機能的な機能や動作は、下記記事をご参照ください。
https://iret.media/104198
ソリューションの概要
今回ご紹介する方法では、スキャンの結果に基づいて、安全なオブジェクトとマルウェア感染が疑われるオブジェクトを分離させます。
バケットを分離することにより、影響範囲を明確にするとともに、他のオブジェクトやシステムへの感染拡大のリスクを減らすことができます。
概要は下記のとおりです。
- S3 バケットへのオブジェクトがアップロードされます。
- バケット内で新しいオブジェクトが検出されたら、GuardDuty Malware Protection for S3 がオブジェクトをスキャンします。
- EventBridge ルールがオブジェクトのスキャン完了を検知したら、オブジェクトを配布する Lambda を実行します。
- 実行される Lambda 関数では、スキャン結果に基づいて、オブジェクトを移動します。
- 安全(NO_THREATS_FOUND)と判定された場合
安全なバケットにオブジェクトを移動 - それ以外の判定の場合(THREATS_FOUND、FAILED など)
隔離バケットにオブジェクトを移動
- 安全(NO_THREATS_FOUND)と判定された場合
- 安全でないファイルの場合、検知した旨をメールで通知します。

自動隔離環境の実装
GuardDuty Malware Protection for S3 の有効化 と S3 の設定
スキャン対象バケットに対して、GuardDuty Malware Protection S3 を有効化します。

Lambda の設定
Lambda 実行ロールを作成します。

Lambdaの実行ロールには下記のポリシーをアタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "SourceBucket",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectTagging",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::xxxxxxxxx-xxxx-scan-bucket/*"
]
},
{
"Sid": "DestBucket",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:PutObjectTagging"
],
"Resource": [
"arn:aws:s3:::xxxxxxxxx-xxxx-clean-bucket/*",
"arn:aws:s3:::xxxxxxxxx-xxxx-quarantine-bucket/*"
]
},
{
"Sid": "LogStreams",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
"arn:aws:logs:*:999999999999:log-group:*:log-stream:*"
]
},
{
"Sid": "LogGroup",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup"
],
"Resource": [
"arn:aws:logs:*:999999999999:log-group:*"
]
},
{
"Sid": "SNS",
"Effect": "Allow",
"Action": [
"sns:Publish"
],
"Resource": [
"arn:aws:sns:ap-northeast-1:999999999999:xxxxxxxxx-xxxx-notify-topic"
]
}
]
}
Lambda の設定を行います。
コードの内容としては、スキャン結果に基づいてオブジェクトを移動するものです。
マルウェアが疑われる場合は、SNS への通知も行います。
下記がサンプルです。
import os
import boto3
import logging
import json
logger = logging.getLogger()
logger.setLevel(logging.INFO)
s3 = boto3.client('s3')
sns = boto3.client('sns')
SOURCE_BUCKET=os.environ.get('SOURCE_BUCKET')
SAFE_BUCKET=os.environ.get('SAFE_BUCKET')
QUARANTINE_BUCKET=os.environ.get('QUARANTINE_BUCKET')
SNS_TOPIC_ARN=os.environ.get('SNS_TOPIC_ARN')
def lambda_handler(event, context):
try:
# 環境変数が設定されているか確認
if not SOURCE_BUCKET or not SAFE_BUCKET or not QUARANTINE_BUCKET or not SNS_TOPIC_ARN:
raise ValueError('Environment variables SOURCE_BUCKET, SAFE_BUCKET, QUARANTINE_BUCKET, or SNS_TOPIC_ARN are not set.')
source_key=event['detail']['s3ObjectDetails']['objectKey']
scan_result=event['detail']['scanResultDetails']['scanResultStatus']
except ValueError as ve:
logger.error(f'ValueError: {ve}')
raise ve
except KeyError as ke:
logger.error(f'value is null or key does not exist: {ke}')
raise ke
copy_source = {'Bucket': SOURCE_BUCKET, 'Key': source_key}
dest_bucket = SAFE_BUCKET if scan_result == 'NO_THREATS_FOUND' else QUARANTINE_BUCKET
if scan_result != 'NO_THREATS_FOUND':
# SNS通知
try:
sns.publish(
TopicArn=SNS_TOPIC_ARN,
Message=json.dumps(event),
Subject="GuardDuty Malware Protection Object Scan Result"
)
except Exception as e:
logger.error(f'Error SNS Publish {SNS_TOPIC_ARN}: {e}')
raise e
try:
# オブジェクトコピー、削除
s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=source_key)
s3.delete_object(Bucket=SOURCE_BUCKET, Key=source_key)
except Exception as e:
logger.error(f'Error move object {source_key} from {SOURCE_BUCKET} to {dest_bucket}: {e}')
raise e
通知用 SNS トピック の設定
SNS トピックを作成します。
サブスクリプションには、メールへの通知を設定しました。

EventBridge の設定
EventBridge から Lambda を実行するロールを作成します。

下記のポリシーをアタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "invokelambda",
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:ap-northeast-1:999999999999:function:xxxxxxxxx-xxxx-move-object"
}
]
}
Event Bridge ルール を作成します。
イベントパターンは、GuardDuty Malware Protection for S3 のスキャン完了のイベントを検出するように設定します。
下記がサンプルです。
{
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Malware Protection Object Scan Result"],
"detail": {
"scanStatus": ["COMPLETED"],
"resourceType": ["S3_OBJECT"],
"s3ObjectDetails": {
"bucketName": ["xxxxxxxxx-xxxx-scan-bucket"]
}
}
}
作成した Lambda 関数と、実行ロールを指定し、EventBridge ルールを作成します。

動作確認
EICAR テストファイルを利用して、動作確認を実施しました。
無害なファイルをアップロードした場合
安全なバケットに移動されました。

EICAR テストファイルをアップロードした場合
隔離処理が実行され、EICARテストファイルが隔離されました。

メール通知も受信しました。

まとめ
GuardDuty Malware Protection for S3 の検知に基づいた隔離処理の実装方法のご紹介でした。
今回紹介したように EventBridge や Lambda を組み合わせることで、検知したファイルの隔離や通知も比較的容易に実装することが可能です。
GuardDuty Malware Protection for S3 は安価に利用できるため、S3 バケットのマルウェアスキャン機能として、積極的に使っていきたい機能です。
ご利用の際は、オブジェクトの最大サイズやリージョン毎のバケット数などのクォータがある点はご注意ください。
クォータを超過したり、定期的なスキャンが必要な場合は、下記記事で紹介している Cloud Storage Security Antivirus などのサードパーティ製品をご検討ください。
Cloud Storage Security Antivirus for Amazon S3 を使ってみた
こちらの製品はアイレットでも取扱可能ですので、気になる方は是非ご相談ください!