AWS S3のアクセス許可を実務レベルで設定する4つの方法

AWS S3のアクセス許可設定は、セキュリティと利便性のバランスが重要です。本記事では、IAMポリシー、バケットポリシー、ACL、プリサインドURLの4つの方法を、実装パターンと注意点を交えて解説します。

S3アクセス許可の全体像

S3のアクセス許可制御は多層構造になっており、どの層で許可が下りるかによって実際のアクセス可否が決まります。上流から下流の順に、IAMポリシー → バケットポリシー → ACL → 暗号化設定が評価されます。一つでも拒否されると、最終的には拒否されます。

実務では「最小権限の原則」に従い、必要最小限の権限だけを付与することが推奨されています。

IAMポリシーでユーザー単位のアクセスを制御する

基本的なIAMポリシーの構造

IAMユーザーやロールに対して、S3へのアクセス権限を直接付与する方法が最も一般的です。以下は、特定のバケットへのリード専用アクセスを付与する例です。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-bucket",
        "arn:aws:s3:::my-bucket/*"
      ]
    }
  ]
}

このポリシーでは、my-bucketというバケットの一覧表示とオブジェクトの取得のみが許可されます。削除や更新はできません。

開発環境と本番環境で権限を分ける実装例

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DevBucketAccess",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-dev",
        "arn:aws:s3:::my-app-dev/*"
      ]
    },
    {
      "Sid": "ProdBucketReadOnly",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-prod",
        "arn:aws:s3:::my-app-prod/*"
      ]
    }
  ]
}

開発環境ではフルアクセス、本番環境は読み取り専用という運用パターンです。Sidはステートメントの識別子で、ログやトラブルシューティング時に役立ちます。

バケットポリシーで外部ユーザーや条件付きアクセスを実装する

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

バケットポリシーはバケット側で定義され、IAMユーザーだけでなく、外部のAWSアカウントやサービスプリンシパルのアクセスを許可できます。特定のIPアドレスからのみアクセスを許可する条件指定も可能です。

特定のIPアドレス範囲からのアクセスのみを許可

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "203.0.113.0/24",
            "198.51.100.0/24"
          ]
        }
      }
    }
  ]
}

このポリシーでは、指定されたIPアドレス範囲からのみオブジェクトの取得が可能です。オフィスネットワークやVPN経由でのアクセスを制限する際に有効です。

CloudFrontからのアクセスのみを許可する

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "cloudfront.amazonaws.com"
      },
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-bucket/*",
      "Condition": {
        "StringEquals": {
          "AWS:SourceArn": "arn:aws:cloudfront::123456789012:distribution/ABCDEFG1234567"
        }
      }
    }
  ]
}

CloudFrontなどのサービスを経由した場合のみアクセスを許可する設定です。直接S3にアクセスされることを防げます。

ACL(アクセスコントロールリスト)の現代的な使い方

なぜACLは推奨されていないのか

ACLは古い権限管理方法で、現在AWSはIAMポリシーやバケットポリシーの使用を推奨しています。ACLはprivatepublic-readpublic-read-writeなどの限られた権限種別しかサポートしていないため、細かい制御ができません。

ただし、マルチアカウント環境で別のAWSアカウントに所有されるバケットにオブジェクトを書き込む場合など、特定のシーンでは今でも使われます。

ACLをプログラムで設定する例(AWS SDK for Python Boto3)

import boto3

s3_client = boto3.client('s3')

# オブジェクトをpublic-readで作成
s3_client.put_object(
    Bucket='my-bucket',
    Key='public-file.txt',
    Body=b'This is public content',
    ACL='public-read'  # 誰でも読める
)

# 既存オブジェクトのACLを変更
s3_client.put_object_acl(
    Bucket='my-bucket',
    Key='public-file.txt',
    ACL='private'  # プライベートに変更
)

実務では、ACLではなくバケットポリシーで権限を管理することを強く推奨します。

プリサインドURLで時間限定アクセスを提供する

プリサインドURLの仕組み

プリサインドURLは、特定の期間だけ有効なS3オブジェクトへのアクセスURLを生成します。AWSの認証情報がない外部ユーザーに一時的にファイルをダウンロードさせたい場合に便利です。URLには署名が含まれており、改ざんされると無効になります。

Boto3でプリサインドURLを生成する

import boto3
from datetime import timedelta

s3_client = boto3.client('s3')

# 1時間有効なダウンロードURLを生成
presigned_url = s3_client.generate_presigned_url(
    'get_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'confidential-report.pdf'
    },
    ExpiresIn=3600  # 秒単位(ここでは1時間)
)

print(f"ダウンロードURL: {presigned_url}")

# アップロード用のプリサインドURLを生成
upload_url = s3_client.generate_presigned_url(
    'put_object',
    Params={
        'Bucket': 'my-bucket',
        'Key': 'user-upload-file.txt'
    },
    ExpiresIn=1800  # 30分
)

print(f"アップロードURL: {upload_url}")

生成されたURLは、認証情報がなくても指定期間内であれば使用可能です。ユーザーに直接URLを配布して、一時的なアクセス権限を提供できます。

プリサインドURLの実装時の注意点

URLの有効期限設定は慎重に: ExpiresInを大きく設定しすぎると、セキュリティリスクが高まります。用途に応じて、ダウンロードは1〜24時間程度、アップロードは15分〜1時間程度が目安です。

URLの共有に注意: 生成されたURLが第三者に知られると、認証情報がなくてもアクセスされてしまいます。HTTPSで通信し、ログに記録される際はマスキングを検討してください。

よくあるハマりポイントと解決策

「Access Denied」エラーが返される場合

S3へのアクセスが拒否される場合、チェックすべきポイントを優先順位順に列挙します:

  • IAMポリシーを確認: ユーザーに s3:* 権限があるか、または具体的なアクション(GetObjectなど)が許可されているか
  • バケットポリシーを確認: バケット側で Deny ステートメントがないか。DenyAllow を上書きします
  • ブロックパブリックアクセス設定を確認: AWSコンソール → S3 → バケット → 「パブリックアクセスのブロック」で設定を確認
  • リソースARNが正しいか確認: arn:aws:s3:::bucket-name vs arn:aws:s3:::bucket-name/* の違いに注意(前者はバケット自体、後者はオブジェクト)

AWS CloudTrailを有効化すると、拒否されたAPI呼び出しの詳細ログが記録され、原因特定が容易になります。

IAMとバケットポリシーが両方あると権限が矛盾する

IAMポリシーで Allow、バケットポリシーで Deny の場合、最終的には拒否されます。複数のポリシーが存在する場合、一つでも Deny があればアクセス不可という明示的な拒否優先ルールが適用されます。

使うべき場面と使うべきでない場面

IAMポリシーを使うべき場面

  • AWS内のサービス(EC2、Lambda)からのアクセス
  • 組織内のIAMユーザー、ロールの権限管理
  • 環境(開発、本番)ごとの細かい権限制御

バケットポリシーを使うべき場面

  • 外部のAWSアカウントやサービスプリンシパルへのアクセス許可
  • IPアドレスやリクエスト条件に基づいた制御
  • CloudFrontなど他のAWSサービスを経由したアクセスの制限

プリサインドURLを使うべき場面

  • 認証情報を持たない外部ユーザーへの一時的なアクセス提供
  • ユーザーアップロード機能の実装
  • メール配信前のセキュアなダウンロードリンク生成

ACLを避けるべき理由

ACLは細かい制御ができず、廃止の方向性が示されています。新規実装ではバケットポリシーやIAMポリシーを使用してください。

実務でのセキュリティベストプラクティス

1. 最小権限の原則を徹底する
必要な最小限の権限のみを付与し、不要な権限は削除します。

2. 環境別のバケット分離
開発環境と本番環境のバケットを分離し、本番バケットへのアクセス権限を制限します。

3. 定期的なアクセスレビュー

K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →