S3パブリックアクセス拒否の原因と解決策:バケットレベルの「Block Public Access」が優先される仕組み

S3に画像をアップロードしてオブジェクトACLを「public-read」に設定したのに、URLにアクセスすると「Access Denied」が返ってくる。オブジェクト単体の設定は正しいはずなのに、なぜ拒否されるのか——この問題の原因はほぼ確実に、バケットレベルの「Block Public Access」設定にある。オブジェクトACLより上位のレイヤーが存在することを知らないと、何度設定を変えても解決しない。

TL;DR:S3パブリックアクセス拒否の原因まとめ

確認レイヤー設定項目影響範囲
アカウントレベルS3 Block Public Access(アカウント全体)全バケットに適用
バケットレベルS3 Block Public Access(バケット個別)そのバケット内の全オブジェクト
バケットポリシーPrincipal: * を許可するポリシーの有無ポリシーで指定したリソース
オブジェクトACLpublic-read ACL個別オブジェクト

結論:オブジェクトACLを「public-read」にしても、バケットまたはアカウントレベルの「Block Public Access」が有効な場合、そのACLは無効化される。上位レイヤーの設定が下位を上書きする構造になっている。

S3パブリックアクセス制御の仕組み:なぜオブジェクトACLだけでは不十分か

S3のアクセス制御は複数のレイヤーが重なって機能する。オブジェクトACLはその中で最も下位のレイヤーに位置する。AWSは2018年以降、誤ったパブリック公開によるデータ漏洩を防ぐため「Block Public Access」という上位の制御機構を導入した。この設定が有効な場合、オブジェクトACLやバケットポリシーで「public」を許可しようとしても、Block Public Accessがそれを遮断する。

Block Public Accessには4つの独立した設定項目がある。それぞれが異なるシナリオをカバーしており、全部オフにしないとパブリックアクセスが通らないケースもある。

graph TD A["S3オブジェクトへのHTTPリクエスト"] --> B{"アカウントレベル Block Public Access"} B -->|"有効(true)"| C["403 Access Denied"] B -->|"無効(false)"| D{"バケットレベル Block Public Access"} D -->|"有効(true)"| C D -->|"無効(false)"| E{"バケットポリシーで GetObjectを許可?"} E -->|"許可あり"| G["アクセス許可"] E -->|"許可なし"| F{"Object Ownership ACL有効?"} F -->|"ACL無効 (BucketOwnerEnforced)"| C F -->|"ACL有効"| H{"オブジェクトACL public-read?"} H -->|"Yes"| G H -->|"No"| C style C fill:#d32f2f,color:#fff style G fill:#388e3c,color:#fff
  1. アカウントレベルのBlock Public Access:全バケットに適用される最上位の制御。ここが有効だと個別バケットの設定に関わらずブロックされる。
  2. バケットレベルのBlock Public Access:そのバケット内の全オブジェクトに適用。アカウントレベルがオフでもここが有効なら拒否される。
  3. バケットポリシー評価:Block Public Accessが無効の場合のみ評価される。Principal: * を許可するポリシーが必要。
  4. オブジェクトACL評価:Block Public Accessが無効で、かつACLが有効化されている場合のみ機能する。
Block Public Accessは「安全装置」のようなものだ。オブジェクトACLというトリガーを引いても、安全装置がかかっていれば発射されない。安全装置を外す操作とトリガーを引く操作は別々に行う必要がある。

S3パブリックアクセス拒否の診断手順

ステップ1:アカウントレベルのBlock Public Access状態を確認する

オブジェクトやバケットの設定を見る前に、まずアカウント全体の設定を確認する。ここが有効なら、それ以降の診断は意味をなさない。アカウントレベルの設定はAWS CLIで以下のように取得できる。

aws s3control get-public-access-block \
  --account-id 123456789012

レスポンス例:

{
    "PublicAccessBlockConfiguration": {
        "BlockPublicAcls": true,
        "IgnorePublicAcls": true,
        "BlockPublicPolicy": true,
        "RestrictPublicBuckets": true
    }
}

いずれかの項目がtrueの場合、その項目が何をブロックしているかを理解した上で対処する必要がある。4つの項目の意味は次のとおり。

設定項目ブロック対象
BlockPublicAcls新規のパブリックACL設定をブロック
IgnorePublicAcls既存のパブリックACLを無視(無効化)
BlockPublicPolicyパブリックアクセスを許可するバケットポリシーの設定をブロック
RestrictPublicBucketsパブリックポリシーが設定されたバケットへのアクセスをAWSサービスと認証済みユーザーのみに制限

ステップ2:バケットレベルのBlock Public Access状態を確認する

アカウントレベルがオフでも、バケット個別の設定が有効な場合がある。バケット作成時のデフォルトは全項目trueなので、明示的にオフにしていなければブロックされたままになる。

aws s3api get-public-access-block \
  --bucket your-bucket-name

パブリックアクセスを許可するには、このバケットのBlock Public Accessを無効化する必要がある。

ステップ3:バケットレベルのBlock Public Accessを無効化する

アカウントレベルとバケットレベルの両方の設定がパブリックアクセスをブロックしていないことを確認した上で、バケットの設定を変更する。本番環境では影響範囲を十分に確認してから実行すること。

aws s3api put-public-access-block \
  --bucket your-bucket-name \
  --public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

変更後、再度get-public-access-blockで設定が反映されていることを確認する。

ステップ4:バケットポリシーでパブリックアクセスを許可する(推奨アプローチ)

Block Public Accessを無効化した後、オブジェクトACLではなくバケットポリシーでパブリックアクセスを制御する方法が現在のAWSの推奨に近い。ACLはレガシーな仕組みであり、新規バケットではデフォルトで無効化されている(Object Ownershipの設定による)。

バケットポリシーで特定バケット内の全オブジェクトへのGetObjectを許可する例:

🔽 バケットポリシーのJSONを展開
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::your-bucket-name/*"
    }
  ]
}
aws s3api put-bucket-policy \
  --bucket your-bucket-name \
  --policy file://bucket-policy.json

ステップ5:Object Ownershipの設定を確認する

2023年以降に作成されたバケットでは、Object Ownershipがデフォルトで「Bucket owner enforced」に設定されており、この場合ACLは完全に無効化される。オブジェクトACLを使おうとしている場合、この設定が原因でACLが機能しないことがある。

aws s3api get-bucket-ownership-controls \
  --bucket your-bucket-name

レスポンスにBucketOwnerEnforcedが返ってきた場合、ACLは使用できない。パブリックアクセスにはバケットポリシーを使用すること。

graph LR A["症状:URLアクセスで403"] --> B["誤診:ACLがpublic-readに なっていないと判断"] B --> C["ACLをpublic-readに設定"] C --> D["まだ403が返る"] D --> E["実際の原因: IgnorePublicAcls=true"] E --> F["ACLの値に関わらず S3がACLを評価しない"] F --> G["修正:Block Public Accessを 無効化 + バケットポリシーを設定"] G --> H["アクセス許可"] style A fill:#f57c00,color:#fff style B fill:#d32f2f,color:#fff style E fill:#1565c0,color:#fff style H fill:#388e3c,color:#fff

実際の現場で起きた誤診パターン

「オブジェクトのプロパティを見たら『パブリック』と表示されているのに、URLにアクセスすると403が返ってくる」という問い合わせは珍しくない。コンソールのオブジェクト詳細画面でACLが「public-read」になっていることを確認して、設定は正しいと判断してしまう。

実際の原因はバケットレベルのIgnorePublicAcls: trueだった。この設定が有効な場合、ACLの値がどう設定されていても、S3はそのACLを評価しない。コンソール上でACLが「public-read」と表示されていることは事実だが、それが実際のアクセス制御に反映されているかどうかは別の話だ。

ACLの設定値と、そのACLが実際に評価されるかどうかは独立した状態として管理されている。これを混同すると診断が迷走する。

最小権限の原則:パブリック公開が本当に必要か再考する

S3オブジェクトをパブリックに公開する前に、本当にそれが必要かを確認する。多くのユースケースでは、以下の代替手段がより安全だ。

ユースケース推奨アプローチ
一時的なファイル共有S3 Presigned URLを使用(有効期限付き)
Webサイトの静的コンテンツ配信CloudFront + OAC(Origin Access Control)でS3をプライベートに保つ
アプリケーションからのアクセスIAMロールを使用し、パブリックアクセスは不要
完全なパブリック公開が必要Block Public Accessを無効化 + バケットポリシーで制御

S3パブリックアクセス拒否の解決:まとめと次のステップ

S3のパブリックアクセス拒否は、オブジェクトACLの設定だけを見ていても解決しない。アクセス制御の評価はアカウントレベル→バケットレベル→バケットポリシー→オブジェクトACLの順に行われ、上位レイヤーが下位を上書きする。Block Public Accessはその最上位に位置する安全装置であり、意図的に無効化しない限り機能し続ける。

診断の起点は常にアカウントレベルのBlock Public Access確認から始めること。その後バケットレベル、Object Ownership、バケットポリシーの順に確認することで、原因を体系的に特定できる。

関連する公式ドキュメントとして、Amazon S3 Block Public Accessの使用およびオブジェクト所有権の制御を参照すること。

用語集

用語説明
Block Public AccessS3バケットおよびオブジェクトへのパブリックアクセスをアカウントまたはバケットレベルで制御するAWSの機能。ACLやバケットポリシーより上位で機能する。
オブジェクトACL(Access Control List)個別のS3オブジェクトに対するアクセス権限を定義するリスト。Block Public Accessが有効な場合は無視される。
Object OwnershipS3バケット内のオブジェクトの所有権とACLの有効/無効を制御する設定。'Bucket owner enforced'の場合、ACLは完全に無効化される。
Presigned URL特定のS3オブジェクトへの一時的なアクセスを許可するURL。有効期限と署名が含まれており、バケットをパブリックにせずにファイルを共有できる。
Origin Access Control(OAC)CloudFrontからS3バケットへのアクセスを制御する仕組み。S3バケットをプライベートに保ちながらCloudFront経由でコンテンツを配信できる。

コメント

このブログの人気の投稿

EC2 SSH接続タイムアウトの原因と修正方法 — セキュリティグループのインバウンドルール完全ガイド

EC2インスタンスIDをメタデータから取得する方法 — IMDSv2が安全な理由