[アップデート]GuardDuty Malware Protection for S3のEventBridge通知にstatusReasonsフィールドが追加されました
はじめに
GuardDuty Malware Protection for S3 について調査していたところ、EventBridge 通知に statusReasons フィールドが追加されていました。
Updated functionality - Malware Protection for S3
The EventBridge notification for S3 object scan results now includes a statusReasons field in scanResultDetails when a scan is skipped. This field provides the specific reason why the scan was skipped. For more information, see S3 object potential scan status and result status.
Document history for Amazon GuardDuty - Amazon GuardDuty
GuardDuty Malware Protection for S3 は、S3 バケットにアップロードされたオブジェクトを自動でマルウェアスキャンしてくれるサービスです。
スキャン結果は EventBridge 通知で受け取れるのですが、スキャンがスキップされた場合に「なぜスキップされたのか」がわからないという課題がありました。
statusReasons フィールドが追加され、スキップ理由が EventBridge 通知のペイロードで確認できるようになりました。
何が嬉しいのか
これまでは scanStatus: SKIPPED の通知を受け取っても、scanResultStatus が UNSUPPORTED か ACCESS_DENIED かはわかる程度でした。
これがアップデートのstatusReasons の追加で、通知ペイロードだけで具体的な理由コードがわかるようになっています。
statusReasons は新規追加フィールドなので、既存の EventBridge ルールパターンはそのまま動作します。
追加の設定も不要で、GuardDuty が自動的にペイロードに含めてくれます。
statusReasons全コード一覧
statusReasons は scanStatus が SKIPPED の場合のみ値が入ります。COMPLETED や FAILED の場合は null です。
全コードの一覧は以下ドキュメントに記載があります。
Monitoring S3 object scans in Malware Protection for S3 - Amazon GuardDuty
ACCESS_DENIED系
| コード | 説明 | 対処法 |
|---|---|---|
UNAUTHORIZED_TO_GET_OBJECT |
IAMロールにs3:GetObject権限がない、またはオブジェクトが存在しない | IAMロールポリシーとKMSポリシーを確認 |
UNAUTHORIZED_TO_ASSUME_ROLE |
GuardDutyがIAMロールをAssumeできない | ロールの信頼ポリシーを確認 |
SSE_C_ENCRYPTED_OBJECT |
SSE-C暗号化オブジェクトはスキャン不可 | SSE-S3/SSE-KMSへの移行を検討 |
OBJECT_E_TAG_CHANGED |
スキャン開始後にETagが変化した | 次回アップロード時に自動再スキャンされるため対処不要 |
BUCKET_NOT_FOUND |
保護対象バケットが削除済み | Malware Protection Planを再設定 |
UNSUPPORTED系
| コード | 説明 | 対処法 |
|---|---|---|
UNSUPPORTED_STORAGE_CLASS |
Glacier等のサポート外ストレージクラス | Standardクラスに変更 |
OBJECT_SIZE_LIMIT_EXCEEDED |
ファイルサイズ上限(100 GB)超過 | ファイル分割を検討 |
PASSWORD_PROTECTED |
パスワード保護されたファイル | セキュリティチームへ確認依頼 |
EXTRACTED_FILE_LIMIT_EXCEEDED |
アーカイブ内ファイル数が上限(10,000)超過 | アーカイブ前にスキャンを推奨 |
EXTRACTED_LEVEL_LIMIT_EXCEEDED |
アーカイブのネスト深度が上限(5)超過 | 圧縮レベルを制限 |
EXTRACTED_BYTE_LIMIT_EXCEEDED |
展開後バイトサイズが上限(100 GB)超過 | ファイル分割を検討 |
EXTRACTION_RATIO_LIMIT_EXCEEDED |
圧縮率が極端に高い(ZIP爆弾対策) | セキュリティアラート対象 |
やってみる
パスワード付き ZIP ファイルをアップロードして、実際に EventBridge 通知で statusReasons が返ってくることを確認してみます。
CloudWatch Logs にイベントを出力して確認します。
S3バケットの作成
テスト用の S3 バケットを作成します。
BUCKET_NAME="guardduty-malware-test-$(date +%Y%m%d)-${RANDOM}"
REGION="ap-northeast-1"
aws s3 mb s3://${BUCKET_NAME} --region ${REGION}
IAMロールの作成
Malware Protection for S3 がスキャンに使用する IAM ロールを作成します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "malware-protection-plan.guardduty.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
aws iam create-role \
--role-name GuardDutyMalwareS3ScanRole \
--assume-role-policy-document file://trust-policy.json
続いて、S3 バケットへのアクセス権限や EventBridge 管理ルールの権限を付与します。
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowGetObject",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketLocation",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::${BUCKET_NAME}",
"arn:aws:s3:::${BUCKET_NAME}/*"
]
},
{
"Sid": "AllowTagging",
"Effect": "Allow",
"Action": [
"s3:PutObjectTagging",
"s3:GetObjectTagging",
"s3:PutObjectVersionTagging",
"s3:GetObjectVersionTagging"
],
"Resource": "arn:aws:s3:::${BUCKET_NAME}/*"
},
{
"Sid": "AllowEventBridge",
"Effect": "Allow",
"Action": [
"s3:PutBucketNotification",
"s3:GetBucketNotification"
],
"Resource": "arn:aws:s3:::${BUCKET_NAME}"
},
{
"Sid": "AllowManagedRule",
"Effect": "Allow",
"Action": [
"events:PutRule",
"events:DeleteRule",
"events:PutTargets",
"events:RemoveTargets"
],
"Resource": "arn:aws:events:${REGION}:${ACCOUNT_ID}:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*",
"Condition": {
"StringEquals": {
"events:ManagedBy": "malware-protection-plan.guardduty.amazonaws.com"
}
}
},
{
"Sid": "AllowPutValidationObject",
"Effect": "Allow",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::${BUCKET_NAME}/malware-protection-resource-validation-object"
}
]
}
aws iam put-role-policy \
--role-name GuardDutyMalwareS3ScanRole \
--policy-name MalwareScanPolicy \
--policy-document file://scan-policy.json
Malware Protection Planの作成
ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/GuardDutyMalwareS3ScanRole"
aws guardduty create-malware-protection-plan \
--role "${ROLE_ARN}" \
--protected-resource "{\"S3Bucket\": {\"BucketName\": \"${BUCKET_NAME}\"}}" \
--actions '{"Tagging": {"Status": "ENABLED"}}' \
--region ${REGION}
レスポンスで malwareProtectionPlanId が返ってくるので控えておきます。
この時点で設定された対象のバケットが確認できます。

EventBridgeルールの作成
今回は結果をログで確認するので、スキャン結果を CloudWatch Logs に送信する EventBridge ルールを作成します。
aws events put-rule \
--name "GuardDuty-Malware-ScanResult-Test" \
--event-pattern '{
"source": ["aws.guardduty"],
"detail-type": ["GuardDuty Malware Protection Object Scan Result"]
}' \
--state ENABLED \
--region ${REGION}
ターゲットに CloudWatch Logs を設定します。
LOG_GROUP="/aws/events/guardduty-malware-scan-results"
aws logs create-log-group \
--log-group-name ${LOG_GROUP} \
--region ${REGION}
aws events put-targets \
--rule "GuardDuty-Malware-ScanResult-Test" \
--targets "Id=CloudWatchTarget,Arn=arn:aws:logs:${REGION}:${ACCOUNT_ID}:log-group:${LOG_GROUP}" \
--region ${REGION}
パスワード付きZIPをアップロードしてSKIPPEDを発生させる
パスワード付き ZIP ファイルを作成してアップロードします。
echo "test file content" > test-for-zip.txt
zip --password testpass protected.zip test-for-zip.txt
aws s3 cp protected.zip s3://${BUCKET_NAME}/test/protected.zip
パスワード保護されたファイルは Malware Protection for S3 でスキャンできないため、SKIPPED になるはずです。
EventBridge通知でstatusReasonsを確認
数分待ってから CloudWatch Logs を確認します。
aws logs filter-log-events \
--log-group-name ${LOG_GROUP} \
--region ${REGION} \
--query 'events[].message' \
--output text | jq .
以下のようなイベントが確認できます。
{
"version": "0",
"id": "07ed0777-041d-4fc2-b715-531f361128ab",
"detail-type": "GuardDuty Malware Protection Object Scan Result",
"source": "aws.guardduty",
"account": "111122223333",
"time": "2026-04-21T00:28:52Z",
"region": "ap-northeast-1",
"resources": [
"arn:aws:guardduty:ap-northeast-1:111122223333:malware-protection-plan/aaced6b73065bb4d60d4"
],
"detail": {
"schemaVersion": "1.0",
"scanStatus": "SKIPPED",
"resourceType": "S3_OBJECT",
"s3ObjectDetails": {
"bucketName": "guardduty-malware-test-20260421-26391",
"objectKey": "test/protected.zip",
"eTag": "6741c9decf4e31ddd6ac768e21f39686",
"versionId": null,
"s3Throttled": false
},
"scanResultDetails": {
"scanResultStatus": "UNSUPPORTED",
"threats": null,
"statusReasons": [
"PASSWORD_PROTECTED"
]
}
}
}
scanResultDetails.statusReasons に PASSWORD_PROTECTED が入っていますね。

scanResultStatus: UNSUPPORTED だけではわからなかった詳細な理由が、statusReasons で明確になりました。
「パスワード保護されたファイルだからスキップされた」と即座に判断できますね。
S3 オブジェクトのタグも確認してみます。
aws s3api get-object-tagging \
--bucket ${BUCKET_NAME} \
--key test/protected.zip
{
"TagSet": [
{
"Key": "GuardDutyMalwareScanStatus",
"Value": "UNSUPPORTED"
}
]
}
タグ側には UNSUPPORTED が記録されています。タグはあくまでスキャン結果のサマリで、詳細な理由コードは EventBridge 通知の statusReasons で確認する形になります。
まとめ
GuardDuty Malware Protection for S3 の EventBridge 通知に statusReasons フィールドが追加されました。
スキャンのスキップ理由を特定できるようになっています。既存環境への影響はなく、追加設定も不要です。
スキップ理由に応じた自動対処フローを組むことで、セキュリティ運用の効率化に役立ちそうです。
Malware Protection for S3 をお使いの方は、ぜひ EventBridge ルールで statusReasons を活用してみてください。
以上、鈴木純がお送りしました。
参考
- ついにブロックができるようになった!Amazon GuardDuty Malware Protection for Amazon S3が発表されました! #AWSreInforce | DevelopersIO
- GuardDuty Malware Protection for S3
- Monitoring S3 object scans in Malware Protection for S3
- Monitoring S3 object scans with Amazon EventBridge
- Enabling Malware Protection for S3 for your bucket
- Capabilities of Malware Protection for S3







