Amazon S3はデフォルトですべてのリソースがプライベートに設定されていますが、実際の運用ではさまざまなユーザーやサービスからのアクセスを適切に制御する必要があります。この記事では、S3のアクセス制御メカニズムであるバケットポリシー、IAMポリシー、ブロックパブリックアクセス、署名付きURLについて解説し、セキュアなデータ管理を実現するための知識を身につけます。

S3のアクセス制御の全体像

S3には複数のアクセス制御メカニズムがあり、それぞれが異なる役割を担っています。これらを組み合わせることで、きめ細かなアクセス制御を実現します。

アクセス制御メカニズムの種類

S3で利用できる主なアクセス制御メカニズムは以下の通りです。

graph TB
    subgraph "S3アクセス制御メカニズム"
        direction TB
        BP[バケットポリシー<br/>リソースベース]
        IAM[IAMポリシー<br/>アイデンティティベース]
        BPA[ブロックパブリックアクセス<br/>ガードレール]
        PRE[署名付きURL<br/>一時的アクセス]
        ACL[ACL<br/>※非推奨]
    end
    
    BP --> |バケット・オブジェクトに<br/>アタッチ| S3[S3バケット]
    IAM --> |IAMユーザー・ロールに<br/>アタッチ| User[IAMユーザー/ロール]
    User --> S3
    BPA --> |パブリックアクセスを<br/>ブロック| S3
    PRE --> |一時的な<br/>アクセス許可| S3
    ACL -.-> |レガシー| S3

各メカニズムの特徴を整理します。

メカニズム タイプ 主な用途
バケットポリシー リソースベース クロスアカウントアクセス、特定条件でのアクセス制御
IAMポリシー アイデンティティベース 同一アカウント内のユーザー・ロールへの権限付与
ブロックパブリックアクセス ガードレール 意図しないパブリック公開の防止
署名付きURL 一時的アクセス 限定的・時間制限付きのオブジェクト共有
ACL レガシー ※2023年4月以降、デフォルトで無効

リクエスト評価の仕組み

S3へのリクエストは、複数のポリシーが組み合わさって評価されます。基本的な評価ルールは「明示的な拒否が最優先」です。

flowchart TD
    A[S3へのリクエスト] --> B{明示的な<br/>Denyがある?}
    B -->|Yes| C[アクセス拒否]
    B -->|No| D{明示的な<br/>Allowがある?}
    D -->|No| E[暗黙的な拒否<br/>アクセス拒否]
    D -->|Yes| F{ブロックパブリック<br/>アクセスで<br/>ブロック?}
    F -->|Yes| C
    F -->|No| G[アクセス許可]

バケットポリシーでアクセスを制御する

バケットポリシーは、S3バケットにアタッチするリソースベースのポリシーです。JSON形式で記述し、誰が何をできるかを定義します。

バケットポリシーの基本構造

バケットポリシーはIAMポリシーと同じJSON構文を使用します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "StatementId",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": "192.168.1.0/24"
                }
            }
        }
    ]
}

各要素の意味は以下の通りです。

要素 説明
Version ポリシー言語のバージョン “2012-10-17”
Statement ポリシーステートメントの配列 複数のルールを定義可能
Sid ステートメントの識別子(任意) “AllowPublicRead”
Effect 許可(Allow)または拒否(Deny) “Allow”, “Deny”
Principal アクセスを許可/拒否する対象 “*”, “arn:aws:iam::123456789012:user/Alice”
Action 許可/拒否するアクション “s3:GetObject”, “s3:PutObject”
Resource 対象となるリソース “arn:aws:s3:::my-bucket/*”
Condition アクセスを許可する条件(任意) IPアドレス、VPC、暗号化など

実践的なバケットポリシーの例

特定のIAMユーザーにオブジェクトの読み取りを許可する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowSpecificUserRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:user/developer"
            },
            "Action": [
                "s3:GetObject",
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ]
        }
    ]
}

クロスアカウントアクセスを許可する例です。別のAWSアカウントからS3バケットへのアクセスを許可します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "CrossAccountAccess",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::987654321098:root"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/shared/*"
        }
    ]
}

VPCエンドポイントからのアクセスのみを許可する例です。インターネット経由のアクセスを遮断し、VPC内からのアクセスのみを許可します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VPCEndpointOnly",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:SourceVpce": "vpce-1234567890abcdef0"
                }
            }
        }
    ]
}

バケットポリシーの設定方法

AWS CLIを使用してバケットポリシーを設定します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# バケットポリシーをJSONファイルから適用
aws s3api put-bucket-policy \
  --bucket my-bucket \
  --policy file://bucket-policy.json

# 現在のバケットポリシーを確認
aws s3api get-bucket-policy --bucket my-bucket

# バケットポリシーを削除
aws s3api delete-bucket-policy --bucket my-bucket

IAMポリシーとの連携

IAMポリシーはユーザー、グループ、ロールにアタッチするアイデンティティベースのポリシーです。バケットポリシーと組み合わせることで、柔軟なアクセス制御を実現します。

バケットポリシーとIAMポリシーの使い分け

どちらを使うべきかは、アクセス制御の要件によって異なります。

要件 推奨されるポリシー
同一アカウント内のユーザー管理 IAMポリシー
クロスアカウントアクセス バケットポリシー
特定のバケットに対する一元管理 バケットポリシー
ユーザーごとの権限管理 IAMポリシー
条件付きアクセス(IP、VPC等) バケットポリシー
パブリックアクセスの許可 バケットポリシー
graph LR
    subgraph "アカウントA"
        UserA[IAMユーザー]
        RoleA[IAMロール]
        IAMP[IAMポリシー]
        UserA --> IAMP
        RoleA --> IAMP
    end
    
    subgraph "アカウントA S3"
        Bucket[S3バケット]
        BP[バケットポリシー]
        BP --> Bucket
    end
    
    subgraph "アカウントB"
        UserB[IAMユーザー]
    end
    
    IAMP --> Bucket
    UserB --> |クロスアカウント| BP

IAMポリシーの例

S3への読み取り専用アクセスを許可するIAMポリシーの例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "S3ReadOnlyAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion",
                "s3:ListBucket",
                "s3:ListBucketVersions"
            ],
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ]
        }
    ]
}

特定のプレフィックスへのアクセスのみを許可する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPrefixAccess",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/users/${aws:username}/*"
        },
        {
            "Sid": "AllowListBucket",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::my-bucket",
            "Condition": {
                "StringLike": {
                    "s3:prefix": ["users/${aws:username}/*"]
                }
            }
        }
    ]
}

このポリシーでは、IAMポリシー変数${aws:username}を使用して、各ユーザーが自分専用のプレフィックス配下のみにアクセスできるようにしています。

ブロックパブリックアクセスで誤公開を防止する

ブロックパブリックアクセスは、S3バケットの意図しないパブリック公開を防ぐためのガードレール機能です。2023年4月以降に作成されたバケットでは、デフォルトで有効になっています。

ブロックパブリックアクセスの4つの設定

ブロックパブリックアクセスには4つの設定項目があります。

graph TB
    subgraph "ブロックパブリックアクセス設定"
        A[BlockPublicAcls<br/>パブリックACLの追加をブロック]
        B[IgnorePublicAcls<br/>既存のパブリックACLを無視]
        C[BlockPublicPolicy<br/>パブリックポリシーの追加をブロック]
        D[RestrictPublicBuckets<br/>パブリックポリシーがあるバケットへの<br/>アクセスを制限]
    end
設定 説明
BlockPublicAcls 新しいパブリックACLの追加と、パブリックACLを含むオブジェクトのアップロードをブロック
IgnorePublicAcls 既存のパブリックACLを無視し、パブリックアクセスを許可しない
BlockPublicPolicy パブリックアクセスを許可するバケットポリシーの追加をブロック
RestrictPublicBuckets パブリックポリシーがあるバケットへのアクセスを、AWSサービスプリンシパルと認可されたユーザーのみに制限

アカウントレベルとバケットレベルの設定

ブロックパブリックアクセスは、AWSアカウントレベルとバケットレベルの両方で設定できます。アカウントレベルの設定は、そのアカウント内のすべてのバケットに適用されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# アカウントレベルで全ブロックを有効化
aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

# バケットレベルで設定
aws s3api put-public-access-block \
  --bucket my-bucket \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

# 現在の設定を確認
aws s3api get-public-access-block --bucket my-bucket

パブリックアクセスが必要な場合

静的Webサイトホスティングなど、パブリックアクセスが必要な場合は、ブロックパブリックアクセスを無効にする必要があります。ただし、その場合でも最小限の設定変更にとどめ、アクセスログを有効にして監視することを推奨します。

1
2
3
4
5
# 静的Webサイトホスティング用にパブリックアクセスを許可
aws s3api put-public-access-block \
  --bucket my-website-bucket \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=false,RestrictPublicBuckets=false"

署名付きURL(Presigned URL)で一時的なアクセスを許可する

署名付きURLは、プライベートなS3オブジェクトへの一時的なアクセスを許可する仕組みです。URLを知っている人なら誰でも、有効期限内にオブジェクトにアクセスできます。

署名付きURLの仕組み

sequenceDiagram
    participant App as アプリケーション
    participant S3 as Amazon S3
    participant User as エンドユーザー
    
    App->>App: 1. 署名付きURLを生成<br/>(IAM認証情報で署名)
    App->>User: 2. 署名付きURLを共有
    User->>S3: 3. 署名付きURLでリクエスト
    S3->>S3: 4. 署名を検証<br/>有効期限を確認
    S3->>User: 5. オブジェクトを返却

署名付きURLには以下の情報が含まれます。

要素 説明
バケット名・オブジェクトキー アクセス対象のオブジェクト
HTTPメソッド GET(ダウンロード)、PUT(アップロード)等
有効期限 URLが有効な期間
署名 IAM認証情報に基づく署名

署名付きURLの有効期限

署名付きURLの有効期限は、URL生成に使用する認証情報のタイプによって上限が異なります。

認証情報タイプ 最大有効期限
IAMユーザー(長期認証情報) 7日間
IAMロール(一時認証情報) ロールセッションの有効期限まで
EC2インスタンスプロファイル 約6時間
AWS STS AssumeRole セッション期間(デフォルト1時間)

一時認証情報で生成した署名付きURLは、その認証情報が失効すると、URLの有効期限前でもアクセスできなくなります。

AWS CLIで署名付きURLを生成する

1
2
3
4
5
6
7
# ダウンロード用の署名付きURL(GET)を生成(有効期限1時間)
aws s3 presign s3://my-bucket/path/to/file.pdf --expires-in 3600

# アップロード用の署名付きURL(PUT)を生成
aws s3 presign s3://my-bucket/uploads/new-file.txt \
  --expires-in 3600 \
  --region ap-northeast-1

--expires-inオプションは秒単位で指定します。3600秒で1時間、86400秒で24時間です。

AWS SDK(Python boto3)で署名付きURLを生成する

Pythonのboto3を使用した署名付きURLの生成例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import boto3
from botocore.exceptions import ClientError

def generate_presigned_url(bucket_name, object_key, expiration=3600, http_method='get_object'):
    """署名付きURLを生成する"""
    s3_client = boto3.client('s3', region_name='ap-northeast-1')
    
    try:
        url = s3_client.generate_presigned_url(
            http_method,
            Params={
                'Bucket': bucket_name,
                'Key': object_key
            },
            ExpiresIn=expiration
        )
        return url
    except ClientError as e:
        print(f"Error generating presigned URL: {e}")
        return None

# ダウンロード用URL
download_url = generate_presigned_url(
    'my-bucket',
    'reports/monthly-report.pdf',
    expiration=3600
)

# アップロード用URL
upload_url = generate_presigned_url(
    'my-bucket',
    'uploads/user-file.txt',
    expiration=300,
    http_method='put_object'
)

署名付きURLのセキュリティ対策

署名付きURLはベアラートークンのように機能するため、URLを知っている人は誰でもアクセスできます。以下の対策を検討してください。

バケットポリシーで署名の有効期間を制限する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "LimitPresignedURLAge",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/*",
            "Condition": {
                "NumericGreaterThan": {
                    "s3:signatureAge": "600000"
                }
            }
        }
    ]
}

この例では、署名が作成されてから10分(600,000ミリ秒)を超えたリクエストを拒否します。CLIやSDKで長い有効期限を設定しても、バケットポリシーで制限できます。

IPアドレスを制限する例です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "IPRestriction",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": ["203.0.113.0/24", "198.51.100.0/24"]
                }
            }
        }
    ]
}

ACLが非推奨となった背景

ACL(Access Control List)は、S3の初期から存在するアクセス制御メカニズムですが、2023年4月以降に作成されたバケットではデフォルトで無効になっています。

ACL無効化の理由

ACLには以下の問題点がありました。

graph TB
    subgraph "ACLの問題点"
        A[複雑な所有権モデル]
        B[クロスアカウントアップロード時の<br/>所有権問題]
        C[バケットポリシーとの<br/>整合性管理が困難]
        D[監査・コンプライアンスの<br/>複雑化]
    end
  1. 所有権の問題: 別のアカウントがオブジェクトをアップロードすると、そのアカウントがオブジェクトを所有し、バケット所有者がアクセスできない状況が発生
  2. ポリシーとの重複: バケットポリシーとACLの両方でアクセス制御を管理すると、どちらが適用されているか把握しづらい
  3. セキュリティリスク: 意図せずパブリックACLを設定してしまうリスク

オブジェクト所有権の設定

現在のS3では、「オブジェクト所有権」設定でACLの有効/無効を制御します。

設定 ACL オブジェクト所有者
バケット所有者の強制(デフォルト) 無効 常にバケット所有者
バケット所有者優先 有効 bucket-owner-full-control ACLが指定された場合はバケット所有者
オブジェクトライター 有効 オブジェクトをアップロードしたアカウント
1
2
3
4
5
6
7
# オブジェクト所有権の設定を確認
aws s3api get-bucket-ownership-controls --bucket my-bucket

# バケット所有者の強制を設定(ACL無効)
aws s3api put-bucket-ownership-controls \
  --bucket my-bucket \
  --ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'

新規でS3バケットを作成する場合は、デフォルトの「バケット所有者の強制」設定のままACLを無効にし、バケットポリシーとIAMポリシーでアクセス制御を行うことを推奨します。

アクセス制御のベストプラクティス

S3のセキュリティを確保するためのベストプラクティスをまとめます。

最小権限の原則

必要最小限のアクセス権限のみを付与します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "MinimalPermissions",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:role/AppRole"
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/app-data/*"
        }
    ]
}

s3:*のような広範なアクションは避け、必要なアクション(s3:GetObjects3:PutObject等)のみを明示的に許可します。

暗号化の強制

バケットポリシーで暗号化されていないオブジェクトのアップロードを拒否します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyUnencryptedUploads",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-bucket/*",
            "Condition": {
                "StringNotEquals": {
                    "s3:x-amz-server-side-encryption": ["AES256", "aws:kms"]
                }
            }
        }
    ]
}

HTTPS通信の強制

HTTP(非暗号化)でのアクセスを拒否します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DenyInsecureTransport",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ],
            "Condition": {
                "Bool": {
                    "aws:SecureTransport": "false"
                }
            }
        }
    ]
}

アクセスログの有効化

サーバーアクセスログを有効にして、誰がいつ何にアクセスしたかを記録します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# ログ用バケットを作成
aws s3 mb s3://my-bucket-logs

# ログ配信用のACLを設定(ログ配信にはACLが必要)
aws s3api put-bucket-acl \
  --bucket my-bucket-logs \
  --grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery \
  --grant-read-acp URI=http://acs.amazonaws.com/groups/s3/LogDelivery

# サーバーアクセスログを有効化
aws s3api put-bucket-logging \
  --bucket my-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "my-bucket-logs",
      "TargetPrefix": "access-logs/"
    }
  }'

IAM Access Analyzerの活用

IAM Access Analyzer for S3を使用して、パブリックアクセスや共有アクセスを許可しているバケットを検出します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# アナライザーを作成(リージョンごとに必要)
aws accessanalyzer create-analyzer \
  --analyzer-name my-s3-analyzer \
  --type ACCOUNT \
  --region ap-northeast-1

# 検出結果を確認
aws accessanalyzer list-findings \
  --analyzer-arn arn:aws:access-analyzer:ap-northeast-1:123456789012:analyzer/my-s3-analyzer \
  --filter '{"resourceType": {"eq": ["AWS::S3::Bucket"]}}'

まとめ

この記事では、Amazon S3のアクセス制御とセキュリティについて解説しました。

  • アクセス制御の全体像: バケットポリシー、IAMポリシー、ブロックパブリックアクセス、署名付きURLの役割と使い分け
  • バケットポリシー: リソースベースのポリシーでクロスアカウントアクセスや条件付きアクセスを制御
  • IAMポリシーとの連携: アイデンティティベースのポリシーで同一アカウント内のユーザー権限を管理
  • ブロックパブリックアクセス: 意図しないパブリック公開を防ぐガードレール
  • 署名付きURL: 一時的・時間制限付きのオブジェクト共有
  • ACLの非推奨化: バケットポリシーとIAMポリシーによる一元管理が推奨

S3のセキュリティは、複数のメカニズムを組み合わせて「多層防御」を実現することが重要です。次回の記事では、S3のストレージクラスとライフサイクル管理によるコスト最適化について詳しく解説します。

参考リンク