S3 入門 — バケットポリシーとアクセス制御を理解する
概要
Amazon S3 はシンプルに見えて、アクセス制御の仕組みが複数レイヤーに分かれているため、入門者がつまずきやすいサービスです。「意図せず公開してしまった」「正しく設定したつもりがアクセスできない」といった事故の多くは、各制御レイヤーの関係を理解していないことに起因します。
本記事では、S3 のアクセス制御を構成する以下の要素を整理し、実際のポリシー例とともに最小権限での設計方針を解説します。
- IAM ポリシー(ユーザー・ロール側)
- バケットポリシー(リソース側)
- ブロックパブリックアクセス
- ACL(レガシー)
掲載するポリシーは公開ドキュメントに基づく一般例であり、特定の本番構成を示すものではありません。
S3 のアクセス制御は「複数レイヤーの合成」で決まる
S3 へのリクエストが許可されるかは、複数の制御を合成した結果で決まります。
| レイヤー | 誰が設定するか | 役割 |
|---|---|---|
| IAM ポリシー | リクエスト元(ユーザー/ロール)側 | このプリンシパルは何ができるか |
| バケットポリシー | リソース(バケット)側 | このバケットに誰が何をできるか |
| ブロックパブリックアクセス | バケット/アカウント | 公開設定の最終防波堤 |
| ACL | バケット/オブジェクト | レガシー。原則無効化推奨 |
基本原則は次の2つです。
- 明示的な Deny が1つでもあれば拒否(Deny が最優先)
- Deny が無い場合、IAM かバケットポリシーの いずれか で Allow されていればアクセス可(同一アカウント内)
この「Deny 優先」「Allow はいずれかで足りる」という評価ルールが、入門時に最も誤解されやすいポイントです。
IAM ポリシーとバケットポリシーの違い
- IAM ポリシー: 主語はプリンシパル。「このユーザー/ロールが操作できる対象」を定義する。
- バケットポリシー: 主語はリソース。
Principalを明示でき、クロスアカウントや条件付き制御を表現できる。
IAM ポリシーの例(特定バケットへの読み書き)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListTheBucket",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-bucket"
},
{
"Sid": "ReadWriteObjects",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::example-bucket/*"
}
]
}
ここで重要なのは、バケット ARN(arn:aws:s3:::example-bucket)とオブジェクト ARN(.../*)を区別する 点です。ListBucket はバケットに対するアクション、GetObject/PutObject はオブジェクトに対するアクションなので Resource の指定が異なります。混同すると「一覧は取れるがダウンロードできない」といった事象が起きます。
バケットポリシーの実例
例1: HTTPS 通信以外を拒否する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
],
"Condition": {
"Bool": { "aws:SecureTransport": "false" }
}
}
]
}
Deny と Condition を組み合わせ、「非 SSL 通信だけを拒否する」という条件付き Deny を表現しています。Deny は最優先なので、他で Allow されていても条件に合致すれば拒否されます。
例2: 特定の VPC エンドポイント経由のみ許可する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnlessFromVpce",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::example-bucket",
"arn:aws:s3:::example-bucket/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": "vpce-xxxxxxxx"
}
}
}
]
}
Allow のホワイトリストより、Deny + StringNotEquals の除外条件で書くほうが抜け漏れが起きにくくなります。
ブロックパブリックアクセスは「最後の砦」
意図しない公開を防ぐのが ブロックパブリックアクセス(Block Public Access) です。4つの設定からなります。
| 設定 | 内容 |
|---|---|
BlockPublicAcls |
公開 ACL の付与を拒否 |
IgnorePublicAcls |
既存の公開 ACL を無視 |
BlockPublicPolicy |
公開バケットポリシーの設定を拒否 |
RestrictPublicBuckets |
公開ポリシーがあってもアクセスを制限 |
特段の理由がなければ、アカウントレベル・バケットレベルの両方で全て有効にするのが基本方針です。
aws s3api put-public-access-block \ --bucket example-bucket \ --public-access-block-configuration \ BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
静的サイトホスティングなどで公開が必要な場合でも、まず「CloudFront + OAC(Origin Access Control)経由にして、バケット自体は非公開のまま」という構成を検討するのが安全です。
確認方法
# 一覧取得・オブジェクト取得(許可されるべき操作) aws s3 ls s3://example-bucket/ aws s3 cp s3://example-bucket/sample.txt ./sample.txt # 公開状態の確認 aws s3api get-public-access-block --bucket example-bucket aws s3api get-bucket-policy-status --bucket example-bucket # IsPublic: false を確認
アクセスできるべきでないケースで AccessDenied が返ること、できるべきケースで成功することの 両方 を確認します。「拒否されるべきものが拒否される確認」まで行うのが、アクセス制御検証の勘所です。
注意点
- Deny は最優先。Allow を足しても Deny があれば通らない。トラブル時はまず Deny を疑う。
- ListBucket と GetObject で Resource が異なる。バケット ARN とオブジェクト ARN(
/*)を取り違えない。 - ACL は原則使わない。「Bucket owner enforced」を有効化し ACL を無効にするのが推奨。
- 公開が必要でも、まず CloudFront + OAC を検討。バケット直公開は最終手段。
- 本番ではまず読み取り権限から。書き込み・削除は影響範囲を確認してから付与する。
まとめ
S3 のアクセス制御は、IAM ポリシー(誰が)・バケットポリシー(このバケットに誰が・どの条件で)・ブロックパブリックアクセス(公開の最終防波堤)という複数レイヤーの合成で決まります。「Deny 優先・Allow はいずれかで足りる」という評価ルールと、「ListBucket と GetObject の Resource の違い」を押さえるだけで、入門時のつまずきの大半は回避できます。最小権限を基本に、公開は BPA で守りつつ必要なら CloudFront 経由で、という設計を習慣化しましょう。
