AWS S3のアクセス拒否エラーを迅速に解決する実践的な診断フロー

本記事では、AWS S3でよく発生する「Access Denied」エラーの具体的な原因特定方法と解決手順を、実際のIAMポリシー設定例を交えて解説します。エラー発生時に最初に確認すべき項目から、複雑な権限設定まで、段階的に対応できる実践的なガイドです。

S3アクセス拒否エラーが発生する5つの主要原因

S3へのアクセス拒否エラーは、単一の原因ではなく、複数の権限レイヤーが関係しています。以下の優先順位で確認することが問題解決の鍵となります。

1. IAMユーザー/ロールの権限不足

最も一般的な原因です。IAMユーザーまたはAssumeしているIAMロールに、S3のGetObject、PutObject、ListBucketなどの必要なアクション権限がない状態です。

2. バケットポリシーによる拒否

バケット自体に設定されたポリシーで、特定のプリンシパルからのアクセスが明示的に拒否されている場合があります。

3. S3ブロックパブリックアクセス設定

アカウントレベルやバケットレベルで「Block all public access」が有効になっていると、IAMポリシーでどうかかわらず、パブリックアクセスは常にブロックされます。

4. KMS暗号化キーへのアクセス権限がない

サーバー側の暗号化(SSE-KMS)を使用している場合、S3の権限だけでなくKMSキーへの復号権限も必要です。

5. VPCエンドポイント設定またはネットワーク制限

バケットポリシーで特定のVPCエンドポイント経由のみを許可している場合、それ以外の接続経路はブロックされます。

段階的な診断と解決手順

ステップ1: まず確認すべき基本事項

エラーが発生した際、以下の情報をログから収集してください。AWS CloudTrailとCloudWatch Logsが重要な情報源です。


# CloudTrailでエラーの詳細を確認
# AWS Management Consoleで以下の流れ:
# CloudTrail > Event history > Filter by Event name: GetObject / PutObject
# または AWS CLI で:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \
  --max-results 10 \
  --region us-east-1

# レスポンス例でエラーメッセージを確認:
# "errorCode": "AccessDenied"
# "errorMessage": "User: arn:aws:iam::123456789012:user/testuser is not authorized to perform: s3:GetObject on resource: arn:aws:s3:::my-bucket/test-file.txt"
  

ステップ2: IAM権限の確認と修正

まず対象ユーザー/ロールに正しいS3権限があるか確認します。以下の最小限のポリシーをテンプレートとして使用してください。


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

AWS CLIで現在のユーザーに附属しているポリシーを確認:


# 現在のユーザーの権限確認
aws iam list-attached-user-policies --user-name testuser

# インラインポリシーの確認
aws iam list-user-policies --user-name testuser

# ポリシーの詳細内容を確認
aws iam get-user-policy \
  --user-name testuser \
  --policy-name policy-name
  

ステップ3: バケットポリシーの確認

バケット自体のポリシーで、該当ユーザーが拒否されていないか確認します。


# バケットポリシーを取得
aws s3api get-bucket-policy --bucket my-bucket

# レスポンス例:
# {
#   "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[...]}",
# }

# ポリシーの内容を整形して確認
aws s3api get-bucket-policy --bucket my-bucket \
  --query Policy \
  --output text | jq .
  

「Deny」ステートメントでアクセスが明示的に拒否されていないか確認してください。

ステップ4: ブロックパブリックアクセス設定の確認


# バケットレベルの設定確認
aws s3api get-public-access-block --bucket my-bucket

# レスポンス例:
# {
#   "PublicAccessBlockConfiguration": {
#     "BlockPublicAcls": true,
#     "IgnorePublicAcls": true,
#     "BlockPublicPolicy": true,
#     "RestrictPublicBuckets": true
#   }
# }

# アカウントレベルの設定確認
aws s3control get-public-access-block \
  --account-id 123456789012
  

ステップ5: KMS暗号化による拒否の確認

SSE-KMSを使用している場合、KMSキーへの権限も確認が必要です。


# オブジェクトのメタデータを確認(暗号化方式を確認)
aws s3api head-object \
  --bucket my-bucket \
  --key test-file.txt

# レスポンス例:
# "ServerSideEncryption": "aws:kms",
# "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/12345678-1234-1234-1234-123456789012"

# KMSキーへのアクセス権限を確認
aws kms describe-key \
  --key-id arn:aws:kms:ap-northeast-1:123456789012:key/12345678-1234-1234-1234-123456789012
  

KMS権限が不足している場合、以下のポリシーをIAMユーザーに追加します。


{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey",
        "kms:DescribeKey"
      ],
      "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/12345678-1234-1234-1234-123456789012"
    }
  ]
}
  

実際のエラーシナリオと解決例

シナリオ1: EC2インスタンスからS3へアクセスできない場合

EC2にアタッチされているIAMロールを確認してください。ロール自体は存在してもポリシーが附属していないケースが多くあります。


# EC2インスタンスにアタッチされているロールを確認
aws ec2 describe-instances \
  --instance-ids i-1234567890abcdef0 \
  --query 'Reservations[0].Instances[0].IamInstanceProfile'

# ロールの詳細確認
aws iam get-role --role-name EC2-S3-Access-Role

# ロールに附属しているポリシーを確認
aws iam list-attached-role-policies \
  --role-name EC2-S3-Access-Role
  

シナリオ2: Lambda関数からS3へのアクセスが拒否される場合

Lambda実行ロール(Lambda Execution Role)に正しいS3権限が付与されているか確認します。


import boto3
import json

s3_client = boto3.client('s3')

def lambda_handler(event, context):
    try:
        # S3からオブジェクトを取得
        response = s3_client.get_object(
            Bucket='my-bucket',
            Key='test-file.txt'
        )
        
        return {
            'statusCode': 200,
            'body': json.dumps('Success')
        }
    
    except s3_client.exceptions.NoSuchBucket as e:
        print(f"バケットが存在しません: {e}")
        return {
            'statusCode': 404,
            'body': json.dumps('Bucket not found')
        }
    
    except Exception as e:
        # AccessDeniedはここで捕捉される
        print(f"エラー詳細: {str(e)}")
        print(f"エラータイプ: {type(e).__name__}")
        
        return {
            'statusCode': 403,
            'body': json.dumps(f'Access Denied: {str(e)}')
        }
  

シナリオ3: クロスアカウントアクセスが失敗する場合

別のAWSアカウントのS3バケットにアクセスする場合、両側の設定が正しく連携している必要があります。


# アクセス元アカウント(IAMユーザーのポリシー):
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::bucket-in-another-account",
        "arn:aws:s3:::bucket-in-another-account/*"
      ]
    }
  ]
}

# アクセス先アカウント(バケットポリシー):
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111:user/testuser"
      },
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::bucket-in-another-account",
        "arn:aws:s3:::bucket-in-another-account/*"
      ]
    }
  ]
}
  

よくある質問

A: このエラーはバケット内のオブジェクト一覧を取得する権限がない状態です。IAMポリシーに以下が含まれているか確認してください。「Resource」はバケット名(スラッシュなし)である点に注意です。

A: これはアクセス拒否ではなく、認証情報が間違っている状態です。IAMアクセスキーID、シークレットアクセスキーが正しいか確認してください。特にキーをコピペした場合は余分なスペースがないか注意してください。

A: AWS CLIで実行しているユーザーの認証情報と、SDKを使用するアプリケーション(EC2ロールやLambda実行ロール)の認証情報が異なる可能性があります。CloudTrailで各々がどの認証情報で実行されているか確認してください。

トラブルシューティング時の便利なコマンド集


# 最も重要:CloudTrailイベントをフィルタリングして詳細を確認
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \
  --start-time 2024-01-15T00:00:00Z \
  --end-time 2024-01-15T23:59:59Z \
  --query 'Events[?ErrorCode==`AccessDenied`]' \
  --output json

# IAMユーザーがAssumeしているロールを確認
aws sts get-caller-identity

# バケット設定全体を一括確認(所有者の権限が必要)
aws s3api get-bucket-tagging --bucket my-bucket
aws s3api get-bucket-versioning --bucket my-bucket
aws s3api get-bucket-encryption
    
K
AWS・Python・生成AIを専門とするソフトウェアエンジニア。AI・クラウド・開発ワークフローの実践ガイドを執筆しています。詳しく見る →