こんにちは。AWS CLIが好きな福島です。
- はじめに
- 結論
- ①Access Analyzer for S3にて検知されていないこと
- ②オブジェクトのACLを設定変更し外部からアクセスできる状態にしていないこと
- ③CloudTrailのデータイベント(S3)の確認(②の確認ができない場合)
- 終わりに
はじめに
早速ですが、Security Hubでは、以下のチェック項目がありチェックをクリアするためには、S3ブロックパブリックアクセスを有効化する必要があります。
S3.8 S3ブロックパブリックアクセス設定は、バケットレベルで有効にする必要があります
※余談ですが、アカウントレベルで有効化しているかチェックする項目(S3.1)もありますので、もし可能であればアカウントレベルで有効化することも検討いただくと良いかと存じます。
ただ、ブロックパブリックアクセスを有効化すると、名前の通りパブリックアクセスが無効化されるため、現在のS3の利用に影響が出る可能性があります。
ということで今回は、安全にS3ブロックパブリックアクセスを有効化できるよう、調査する方法をご紹介いたします。
結論
以下2点を確認いただければ、ブロックパブリックアクセスを有効化しても問題ないです。
- ①Access Analyzer for S3にてパブリックアクセスがtureで検知されていないこと
- ②オブジェクトのACLを設定変更し外部からアクセスできる状態にしていないこと
※1 オブジェクト数が多く②の確認ができない場合、代替案として③CloudTrailのデータイベント(S3)の確認があります。
①Access Analyzer for S3にて検知されていないこと
こちらの確認はすごく簡単です。
そもそも、IAM Access Analyzerとは
まずIAMには、IAM Access AnalyzerというAWSリソースに紐付いているポリシーを検査し、 他AWSアカウントや外部のインターネット等からのアクセスを可能とするような設定がされているか否か、 つまり管理者として"意図せぬ公開設定がされていないか"を検出および可視化してくれる機能があります。
詳細: https://blog.serverworks.co.jp/tech/2020/07/01/iam_access_analyzer-2/
そして、IAM Access AnalyzerでチェックできるサービスにはS3が含まれているため、 この機能を使うことで公開設定の有無を確認することができます。
またこの機能は無料で利用できるため、気軽に設定を有効化できます。 上記ブログに設定方法も記載されているため、有効化していない場合はぜひ有効化してください。
Access Analyzer for S3の確認方法
ここからAccess Analyzer for S3の確認方法を記載いたします。
IAM Access Analyzerへのアクセス
まず、IAM Access Analyzerにアクセスします。
https://ap-northeast-1.console.aws.amazon.com/access-analyzer/home?region=ap-northeast-1#/findings
パブリックアクセスが可能なS3の確認
IAM Access Analyzerでチェックできるサービスは複数あるため、リソースタイプをS3に絞ります。 また、パブリックアクセス可能なS3を絞り込むため、パブリックアクセスを「true」に絞ります。
表示されたS3はパブリックアクセスが可能となっているため、意図したものかを確認し意図したものであれば、ブロックパブリックアクセスは無効化のままにする必要があります。
補足
検出結果の数が多くコンソールからの確認が大変な場合、以下のコマンドでCLIからも取得可能です。
echo "resourceType,resource,error,SourceIp,principal,action[0],action[1],action[2],action[3],action[4],action[5],analyzedAt" > /tmp/awscli.tmp ;\ aws accessanalyzer list-findings \ --analyzer-arn $(aws accessanalyzer list-analyzers --query "analyzers[].arn" --output text) \ --filter '{"resourceType":{"eq":["AWS::S3::Bucket"]}}' \ --query 'findings[?isPublic==`true`].[resourceType,resource,error,condition.["aws:SourceIp"][0],principal.AWS,action[0],action[1],action[2],action[3],action[4],action[5],analyzedAt]' --output text | tr "\t" "," >> /tmp/awscli.tmp ;\ column -s, -t /tmp/awscli.tmp ;\ rm -f /tmp/awscli.tmp
- 実行結果例
resourceType resource error SourceIp principal action[0] action[1] action[2] action[3] action[4] action[5] analyzedAt AWS::S3::Bucket arn:aws:s3:::xxxxx None None * s3:GetObject None None None None None 2023-11-23T09:39:25.743000+00:00 AWS::S3::Bucket arn:aws:s3:::xxxxx None None * s3:GetObject None None None None None 2023-10-25T00:17:43.031000+00:00 AWS::S3::Bucket arn:aws:s3:::xxxxx None None * s3:GetObject s3:GetObjectVersion None None None None 2023-11-23T09:39:25.743000+00:00 AWS::S3::Bucket arn:aws:s3:::xxxxx None None * s3:GetObject s3:GetObjectVersion None None None None 2023-10-29T21:29:11.880000+00:00
②オブジェクトのACLを設定変更し外部からアクセスできる状態にしていないこと
①の方法では、オブジェクトごとのACLの設定により外部公開していることはチェックできないため、 別途オブジェクトのACLの設定を1つずつ確認していく必要があります。
Security Hub(S3.8)でチェックされているS3バケットの一覧を取得
/tmp/check-s3-list.txtにSecurity Hub(S3.8)でチェックされているS3バケットの一覧をを出力します。
aws securityhub get-findings \ --filters "{\"GeneratorId\":[{\"Value\": \"aws-foundational-security-best-practices/v/1.0.0/S3.8\",\"Comparison\":\"PREFIX\"}],\"ComplianceStatus\": [{\"Value\":\"FAILED\",\"Comparison\":\"EQUALS\"}]}" \ --query 'Findings[].[Resources[0].Id]' --output text | sort | uniq | sed 's/arn:aws:s3::://g' > /tmp/check-s3-list.txt
- 実行後のファイルの中身例
# cat check-s3-list.txt awslogs-elb-0123456789012 awslogs-cloudtrail-0123456789012 #
オブジェクト数の確認
オブジェクト数が多い場合、オブジェクトごとのACLの確認が困難なため、事前にオブジェクト数を確認します。
cat /tmp/check-s3-list.txt | while read line do echo "$line $(aws s3api list-objects --bucket $line --query "Contents[].[Key]" --output text | wc -l)" done
- 実行例
awslogs-elb-0123456789012 3 awslogs-cloudtrail-0123456789012 5
※上記結果の2列目がオブジェクト数になりますが、オブジェクトの数が多い場合は、対象のS3バケットは除外(コメントアウト)し、③CloudTrailのデータイベント(S3)の確認を行った方が良いかと存じます。
check-s3-list.txtに記載のバケットのオブジェクトのACLの確認
/tmp/check-s3-list.txtの内容を基にオブジェクトACLを1つずつ確認します。 オブジェクトの外部公開設定されている場合、/tmp/s3-public-object.txtに結果を出力します。
> /tmp/s3-public-object.txt ;\ ## バケット一覧からバケット名を1行ずつ読み込む(#が含まれている行は除外) grep -v "#" /tmp/check-s3-list.txt | while read bucket do echo "## $bucket" ## オブジェクト一覧を取得し、1行ずつ読み込む aws s3api list-objects --bucket $bucket --query "Contents[].[Key]" --output text | while read key do ## オブジェクトACLの設定を確認 result=$(aws s3api get-object-acl --bucket $bucket --key "$key" --query "Grants[].Grantee.URI" --output text) ## 進捗状況が分かるようにオブジェクトパスを出力 echo "s3://$bucket/$key" ## オブジェクトが外部公開されている場合、オブジェクトの設定内容および/tmp/s3-public-object.txtに結果を出力 if [[ ! -z $result ]] ; then echo $result echo "$bucket,s3://$bucket/$key" >> /tmp/s3-public-object.txt fi done done
- 実行結果例
## awslogs-elb-0123456789012 s3://awslogs-elb-0123456789012/fk-test-001/AWSLogs/0123456789012/ELBAccessLogTestFile s3://awslogs-elb-0123456789012/test.txt http://acs.amazonaws.com/groups/global/AllUsers http://acs.amazonaws.com/groups/global/AuthenticatedUsers #
以下のどちらかが出力されたオブジェクトは外部公開設定しているため、ブロックパブリックアクセスは無効化のままにする必要があります。
http://acs.amazonaws.com/groups/global/AllUsers
http://acs.amazonaws.com/groups/global/AuthenticatedUsers
上記の例で言うと、s3://awslogs-elb-0123456789012/test.txtが外部公開されていることになります。
また、/tmp/s3-public-object.txtのファイルに外部公開設定がされているバケットとフルパスを出力するようにしているため、 実行中の画面をずっと見ておく必要はないです。
- イメージ
# cat /tmp/s3-public-object.txt awslogs-elb-0123456789012,s3://awslogs-elb-0123456789012/test.txt #
③CloudTrailのデータイベント(S3)の確認(②の確認ができない場合)
基本的には、①、②を確認すれば良いのですが、オブジェクト数が多く②の確認ができない場合に本確認を行うと良いかと存じます。 これは実際に対象のS3へパブリックアクセスがあるかをログで確認し、ログがなければ問題ないと判断する方法です。
Athenaテーブル作成
CloudTrailの画面からAthenaテーブル作成を行います。
Athenaで分析(参考)
私は以下のクエリでパブリックアクセスの有無を分析いたしました。 ※【テーブル名】を適切な値に変更し実行します。
select eventtime,eventname,sourceipaddress,useragent,errorcode,errormessage, json_extract_scalar(requestParameters, '$.bucketName') as bucketName, json_extract_scalar(requestParameters, '$.key') as object from 【テーブル名】 where eventsource = 's3.amazonaws.com' and sourceipaddress not like '%amazonaws.com' and sourceipaddress != 'AWS Internal' and json_extract_scalar(requestParameters, '$.key') is not null
- 実行結果
パブリックアクセスがある場合は、useragentが以下のようになっているケースが多いと思いますが、 出力された内容を精査し、ブロックパブリックアクセスを有効化するかご検討いただければと存じます。
[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36]
クエリ補足(where句)
今回は、where句を以下の通り設定しましたが、環境に合わせて変更いただければと存じます。
where eventsource = 's3.amazonaws.com' and
⇒ データソースがS3のみに絞ります。
sourceipaddress not like '%amazonaws.com' and sourceipaddress != 'AWS Internal' and
⇒ ソースIPがAWSサービスまたはAWS Internalを除外します。
json_extract_scalar(requestParameters, '$.key') is not null
⇒ バケットへのパブリックアクセスの有無は①の手法で確認できるため、オブジェクトへのアクセスのみに限定しています。
終わりに
今回は、Security Hub対応の一環として、S3ブロックパブリックアクセスを有効化しても問題ないか確認する方法をご紹介いたしました。
どなかたのお役に立てれば幸いです。