S3 バケット
概要
IAM Access Analyzer が S3 バケットの外部アクセスをどのように検出するかを検証する。
S3 バケットは IAM Access Analyzer の対応リソースの中で最も馴染みがあり、デフォルト保護(Block Public Access)、予防コントロール(RCP)、検出(IAM Access Analyzer)の 3 レイヤーすべてが揃っているリソースである。本記事では、各レイヤーの関係を検証を通じて明らかにする。
S3 バケットの外部アクセス制御レイヤー
| レイヤー | S3 バケットの状況 |
|---|---|
| デフォルト保護 | Block Public Access(2023 年 4 月以降、新規バケットでデフォルト有効) |
| 予防(RCP) | CT.S3.PV.4(組織外プリンシパルによるアクセスを拒否)✅ |
| 予防(SCP) | カスタム SCP 不要(RCP でカバー) |
| 検出 | IAM Access Analyzer(パブリック + クロスアカウント)✅ |
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。
検証の流れ
flowchart LR
A[1. 事前準備] --> B[2. パブリックアクセス<br>検出]
B --> C[3. クロスアカウント<br>アクセス検出]
C --> D[4. Security Hub<br>連携確認]
D --> E[5. アーカイブと<br>Security Hub]
E --> F[6. BPA 有効時の<br>finding 変化]
F --> G[7. 予防コントロール<br>との連動]
G --> H[8. S3 ディレクトリ<br>バケット]
H --> I[9. クリーンアップ]
結果
(検証完了後に記載予定)
1. 事前準備
テスト用の S3 バケットを作成する。
aws s3api create-bucket \
--bucket aa-s3-test-<Workload アカウント ID> \
--create-bucket-configuration LocationConstraint=ap-northeast-1パブリックアクセスの検出を検証するため、Block Public Access を無効化する。
aws s3api put-public-access-block \
--bucket aa-s3-test-<Workload アカウント ID> \
--public-access-block-configuration \
"BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
(出力なし)Block Public Access が無効化されたことを確認する。
aws s3api get-public-access-block \
--bucket aa-s3-test-<Workload アカウント ID>2. パブリックアクセスの検出
バケットポリシーに Principal: "*" を設定し、パブリックアクセスを許可する。
aws s3api put-bucket-policy \
--bucket aa-s3-test-<Workload アカウント ID> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::aa-s3-test-<Workload アカウント ID>/*"
}
]
}'
(出力なし)finding が生成されたことを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Auditfinding の詳細を確認する。パブリックアクセスの場合、isPublic が true であることを確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <パブリックアクセス finding ID> \
--region ap-northeast-1 \
--profile Audit3. クロスアカウントアクセスの検出
バケットポリシーに組織外アカウントへの許可を追加する。
aws s3api put-bucket-policy \
--bucket aa-s3-test-<Workload アカウント ID> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::aa-s3-test-<Workload アカウント ID>/*"
},
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::aa-s3-test-<Workload アカウント ID>/*"
}
]
}'
(出力なし)finding を確認する。パブリックアクセスとクロスアカウントアクセスで 2 件の finding が別々に生成される ことを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Auditクロスアカウントアクセスの finding 詳細を確認する。isPublic が false であること、principal に組織外アカウント ID が特定されていることを確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <クロスアカウントアクセス finding ID> \
--region ap-northeast-1 \
--profile Audit4. Security Hub 連携確認
Security Hub に IAM Access Analyzer の finding が連携されていることを確認する。
aws securityhub get-findings \
--filters '{
"ProductName": [{"Value": "IAM Access Analyzer", "Comparison": "EQUALS"}],
"ResourceId": [{"Value": "aa-s3-test", "Comparison": "CONTAINS"}]
}' \
--query 'Findings[].{Title:Title,Severity:Severity.Label,ResourceId:Resources[0].Id,WorkflowStatus:Workflow.Status}' \
--profile Audit以下を確認する。
- パブリックアクセスの finding: Severity が
MEDIUM - クロスアカウントアクセスの finding: Severity が
LOW - ProductName が
IAM Access Analyzer
5. アーカイブと Security Hub の連動
アーカイブ後の Security Hub
パブリックアクセスの finding をアーカイブする。
aws accessanalyzer update-findings \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--ids '["<パブリックアクセス finding ID>"]' \
--status ARCHIVED \
--region ap-northeast-1 \
--profile Audit
(出力なし)Security Hub から finding が消えることを確認する。
aws securityhub get-findings \
--filters '{
"ProductName": [{"Value": "IAM Access Analyzer", "Comparison": "EQUALS"}],
"ResourceId": [{"Value": "aa-s3-test", "Comparison": "CONTAINS"}]
}' \
--query 'Findings[].{Title:Title,Severity:Severity.Label,WorkflowStatus:Workflow.Status}' \
--profile Auditアーカイブした finding が Security Hub から消え、クロスアカウントアクセスの finding のみが残ることを確認する。
ポリシー変更後の再アクティブ化
バケットポリシーを変更(アクションを追加)して、アーカイブした finding が再アクティブ化されるか確認する。
aws s3api put-bucket-policy \
--bucket aa-s3-test-<Workload アカウント ID> \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowPublicReadExpanded",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::aa-s3-test-<Workload アカウント ID>",
"arn:aws:s3:::aa-s3-test-<Workload アカウント ID>/*"
]
},
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::aa-s3-test-<Workload アカウント ID>/*"
}
]
}'
(出力なし)ポリシー変更後、アーカイブされていたパブリックアクセスの finding が再び ACTIVE になるか確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit6. Block Public Access 有効時の finding 変化
Block Public Access を再有効化する。
aws s3api put-public-access-block \
--bucket aa-s3-test-<Workload アカウント ID> \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"
(出力なし)以下の 2 点を確認する。
パブリックアクセスの finding
Block Public Access が有効な状態で、パブリックアクセスの finding がどうなるか確認する。Zelkova の到達可能性分析が BPA を考慮して「実際にはアクセスできない」と判定し、finding が RESOLVED になるか、それとも ACTIVE のまま残るか。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,status:status,resourceType:resourceType}' \
--region ap-northeast-1 \
--profile Auditクロスアカウントアクセスの finding
Block Public Access はパブリックアクセスのみをブロックし、クロスアカウントアクセスには影響しない。クロスアカウントアクセスの finding が引き続き ACTIVE であることを確認する。
これは「BPA を有効にしているから安心」という誤解を解く重要な検証である。BPA ではクロスアカウント共有は防げず、IAM Access Analyzer による検出が必要であることを実証する。
7. 予防コントロール(CT.S3.PV.4)との連動
CT.S3.PV.4 が有効な状態で、バケットポリシーに組織外アカウントへの許可が残っている場合でも、IAM Access Analyzer は finding を出す。RCP はアクセスをブロックするが、バケットポリシー自体は変更しないため、IAM Access Analyzer はポリシーベースの分析で引き続き外部アクセスを検出する。
つまり、予防コントロールでブロックしていても、ポリシーの修正が必要であることを IAM Access Analyzer が示してくれる。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Auditfinding が ACTIVE のまま残っていることを確認する。これにより、「検出→予防→検証」サイクルにおいて、IAM Access Analyzer は予防コントロールの有無に関わらずポリシーの問題を指摘し続ける役割を持つことがわかる。
8. S3 ディレクトリバケット
S3 ディレクトリバケットに対する IAM Access Analyzer の検出を確認する。
ディレクトリバケットの作成
aws s3api create-bucket \
--bucket aa-s3-dir-test-<Workload アカウント ID>--apne1-az4--x-s3 \
--create-bucket-configuration '{
"Location": {
"Type": "AvailabilityZone",
"Name": "apne1-az4"
},
"Bucket": {
"Type": "Directory",
"DataRedundancy": "SingleAvailabilityZone"
}
}'Security Hub CSPM の確認
通常バケット向けの外部アクセスコントロール(S3.2, S3.3 等)がディレクトリバケットに対して finding を出していないことを確認する。
aws securityhub get-findings \
--filters '{
"ResourceId": [{"Value": "aa-s3-dir-test", "Comparison": "CONTAINS"}]
}' \
--query 'Findings[].{Title:Title,Severity:Severity.Label,ResourceId:Resources[0].Id}' \
--profile Audit外部アクセスに関する finding がないことを確認する。
外部アクセスの設定と IAM Access Analyzer の検出
ディレクトリバケットにバケットポリシーで組織外アカウントへの許可を設定し、IAM Access Analyzer の finding が生成されることを確認する。
aws s3api put-bucket-policy \
--bucket aa-s3-dir-test-<Workload アカウント ID>--apne1-az4--x-s3 \
--policy '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
"Action": "s3express:GetObject",
"Resource": "arn:aws:s3express:ap-northeast-1:<Workload アカウント ID>:bucket/aa-s3-dir-test-<Workload アカウント ID>--apne1-az4--x-s3/*"
}
]
}'
(出力なし)finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["aa-s3-dir-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit通常バケットとの finding の違い(リソースタイプが AWS::S3Express::DirectoryBucket であること等)を確認する。
9. クリーンアップ
テスト用リソースを削除する。
S3 ディレクトリバケットの削除
aws s3api delete-bucket \
--bucket aa-s3-dir-test-<Workload アカウント ID>--apne1-az4--x-s3
(出力なし)S3 バケットの削除
バケットポリシーを削除する。
aws s3api delete-bucket-policy \
--bucket aa-s3-test-<Workload アカウント ID>
(出力なし)バケットを削除する。
aws s3api delete-bucket \
--bucket aa-s3-test-<Workload アカウント ID>
(出力なし)finding の確認
リソース削除後、IAM Access Analyzer の finding が自動的に RESOLVED になることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"resource": {"contains": ["aa-s3-test"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit