【実務・中級編】AWS S3バケットのパブリックアクセスブロック設定とACLの無効化 – アプリケーションセキュリティ & 安全な開発防御ガイド

S3バケットは「公開して当たり前」の時代は終わった。現代のインフラエンジニアが守るべき鉄則

現場でコードを叩いていると、「とりあえず開発環境だし、S3のパブリックアクセスをオンにしておけば楽だよね」という甘い囁きが聞こえてくることがある。だが、今の時代、そんな判断は「会社をリスクに晒す直結行為」だ。

S3の流出インシデントは、ハッカーが高度なゼロデイを突いた結果ではない。そのほとんどが、「バケットポリシーの設定ミス」や「古臭いACL運用によるガバナンスの欠如」という、極めて初歩的なミスに起因している。今日は、この「バケットの公開」という地雷を完全に埋め立てるための、現場の流儀を叩き込む。

なぜ「ACL」はもはや敵なのか

かつてS3のアクセス制御にはACL(アクセスコントロールリスト)が使われていた。しかし、今となってはこれはセキュリティ上の負債だ。

ACLは、IAMポリシーと重複して適用されるため、権限の優先順位が複雑になり、監査の目を潜り抜ける「隠れパブリック」を生みやすい。「ACLを無効化し、IAMとバケットポリシーだけで制御する」。これが今のセキュリティ標準だ。

攻撃者から見た「バケットの公開」

攻撃者は、ツール(`s3-inspector`や単純なスクリプト)を使って、全世界のS3バケットをスキャンしている。`http://[バケット名].s3.amazonaws.com` にGETリクエストを投げるだけの単純な作業だ。

もしあなたのバケットが `s3:ListBucket` を許可していれば、攻撃者は「どんなファイルがあるか」のリストを奪う。そこからバックアップデータや環境変数(.env)、AWSの認証情報が含まれた設定ファイルをダウンロードするのは、ほんの数秒の作業だ。

実務で必須の「鉄壁」設定:S3ブロックパブリックアクセス

コンソールやIaCで必ず適用すべき設定がある。これが「S3 ブロックパブリックアクセス」だ。これを強制すれば、万が一バケットポリシーでミスをしても、S3側が強制的にリクエストを拒否してくれる。

Terraformによる「絶対に公開させない」設定サンプル

IaCでインフラを組んでいるなら、以下の設定をベースラインにすること。

S3バケットを作成する際は、以下の設定をセットで定義する
resource “aws_s3_bucket_public_access_block” “secure_bucket_block” {
bucket = aws_s3_bucket.my_app_bucket.id

# 全てのパブリックアクセスをブロック
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

ACLは無効化し、IAMポリシーで制御する
resource “aws_s3_bucket_ownership_controls” “no_acl” {
bucket = aws_s3_bucket.my_app_bucket.id
rule {
object_ownership = “BucketOwnerEnforced” # これによりACLを完全に無効化できる
}
}

アプリ側からの安全なデータ取得(署名付きURLの活用)

「画像を非公開にしたいけど、Webアプリには表示させたい」というケースでは、S3をパブリックにする必要はない。署名付きURL(Presigned URL)を生成し、一時的なアクセス権をアプリ側から付与するのが正攻法だ。

Python (Boto3) による安全な署名付きURL発行例

バックエンドでこの関数を呼び出し、フロントエンドに一時的なURLを返すようにする。

import boto3

def generate_presigned_url(bucket_name, object_name, expiration=3600):
“””
S3のバケットを非公開にしたまま、一時的にファイルを閲覧させるためのURLを発行する
“””
s3_client = boto3.client(‘s3’)
try:
# 指定した時間(秒)だけ有効なURLを発行
response = s3_client.generate_presigned_url(‘get_object’,
Params={‘Bucket’: bucket_name,
‘Key’: object_name},
ExpiresIn=expiration)
except Exception as e:
print(f”Error: {e}”)
return None
return response

使い方:フロントにはこのURLを返す
https://my-bucket.s3.amazonaws.com/private-data.jpg?X-Amz-Signature=…

最後に:エンジニアとしてのマインドセット

セキュリティを「面倒なこと」と捉えるか、「プロフェッショナルとしての品質の一部」と捉えるか。ここで結果に大きな差が出る。

1. デフォルトは拒否: バケットポリシーは「許可」を書くのではなく、基本的には何も書かない(暗黙の拒否)。
2. 最小権限の原則: もし公開が必要なら、CloudFrontを経由させ、OAC(Origin Access Control)を使って「CloudFrontからしかアクセスできないS3」を作る。
3. 自動検知: AWS ConfigやGuardDutyを有効にし、パブリックなバケットが作られた瞬間に通知が飛ぶようにしておくこと。

「設定したつもり」は、セキュリティの世界では「何もしていない」と同じだ。今すぐAWSコンソールを開き、ACLが有効になっていないか、ブロックパブリックアクセスがオフになっていないか、確認してほしい。君の書いたコードが、いつか誰かの人生を狂わせるような情報漏洩の引き金にならないことを祈っている。

現場からは以上だ。次は、IAMロールの権限過多について話そうか。

コメント

タイトルとURLをコピーしました