S3 の 403 エラーでポリシー ARN が表示される条件を確認してみた
はじめに
2026 年 8 月 13 日、Amazon S3 の 403 Access Denied エラーメッセージに、アクセスを拒否したポリシーの ARN が含まれるようになるアップデートがありました。
いわささんの記事では、アイデンティティベースポリシー、アクセス許可境界、SCP(サービスコントロールポリシー)、RCP(リソースコントロールポリシー)による明示的 Deny 時の動作が紹介されています。
本記事では、それらに加えてセッションポリシーやインラインポリシー、バケットポリシーも対象に、ポリシー ARN が表示される条件を確認しました。あわせて、組織外からの無認証リクエストに ARN が露出しないかも検証しています。
検証内容
ARN が表示される種別と表示されないケースを先に一覧し、そのうえで条件を個別に見ていきます。
ポリシー種別ごとに何が返るか
明示的 Deny を作れるポリシー種別ごとに s3:GetObject を拒否させ、返ってくるエラーメッセージを比べました。
| ポリシー種別 | 指定方法 | ポリシー ARN | 備考 |
|---|---|---|---|
| SCP | Organizations で管理アカウントから適用 | ○ | - |
| RCP | Organizations で管理アカウントから適用 | ○ | - |
| アイデンティティベースポリシー | カスタマー管理ポリシーをロールにアタッチ | ○ | - |
| セッションポリシー | --policy-arns でマネージドポリシーを指定 |
○ | - |
| アクセス許可境界 | カスタマー管理ポリシーを境界に設定 | ○ | - |
| セッションポリシー(インライン) | --policy でインライン JSON を指定 |
× | 種別のみ |
| アイデンティティベースポリシー(インライン) | put-role-policy でインライン設定 |
× | 種別のみ |
| バケットポリシー | バケットに直接設定 | × | 種別のみ |
| 暗黙的 Deny(参考) | Allow が存在しない状態 | × | 拒否理由のみ |
| 組織外からの無認証リクエスト(参考) | 署名なし HTTP リクエスト | × | Access Denied のみ |
「ポリシー ARN」の列は、エラーメッセージに拒否したポリシーの ARN が含まれるかどうかを示しています。
検証は同一組織内の 1 アカウントで、s3:GetObject に対して実施しました。S3 ユーザーガイドの例は , with policy ARN: <ARN> という書式ですが、実測したメッセージはすべて : <ARN> の形式でした。
拒否要因が 1 つになるよう、ケースごとに対象バケットを分けています。オブジェクトの取得にはいずれのケースでも同じコマンドを使い、バケット名だけを差し替えました。アカウント ID・ロール名・バケット名・ポリシー ID は例示用の値に置き換えているため、同じ ExampleRole でもケースによって別のロールを指します。
aws s3api get-object \
--bucket <バケット名> \
--key test.txt \
/tmp/out.txt \
--region ap-northeast-1
SCP
管理アカウントで次の SCP を作成し、メンバーアカウントへ直接アタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyS3GetObject",
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-scp/*"
}
]
}
aws organizations attach-policy \
--policy-id p-examplescpid \
--target-id 123456789012
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-scp/test.txt" with an explicit deny in a service control policy: arn:aws:organizations::111122223333:policy/o-exampleorgid/service_control_policy/p-examplescpid
RCP
RCP は SCP と異なり Principal の指定が必要です。管理アカウントで作成し、メンバーアカウントへ直接アタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyS3GetObject",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-rcp/*"
}
]
}
aws organizations attach-policy \
--policy-id p-examplercpid \
--target-id 123456789012
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-rcp/test.txt" with an explicit deny in a resource control policy: arn:aws:organizations::111122223333:policy/o-exampleorgid/resource_control_policy/p-examplercpid
アイデンティティベースポリシー
DenyS3GetObjectIdentity という名前でカスタマー管理ポリシーを作成し、実行に使うロールへアタッチしました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyS3GetObject",
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-identity/*"
}
]
}
aws iam attach-role-policy \
--role-name ExampleRole \
--policy-arn arn:aws:iam::123456789012:policy/DenyS3GetObjectIdentity
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-identity/test.txt" with an explicit deny in an identity-based policy: arn:aws:iam::123456789012:policy/DenyS3GetObjectIdentity
セッションポリシー
DenyS3GetObjectSession をカスタマー管理ポリシーとして作成し、assume-role の --policy-arns で指定しました。セッションポリシーの実効権限はロール側ポリシーとの積集合になるため、対象バケット以外が暗黙的に拒否されないよう Allow を併記しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAll",
"Effect": "Allow",
"Action": "*",
"Resource": "*"
},
{
"Sid": "DenyS3GetObject",
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-session/*"
}
]
}
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/ExampleRole \
--role-session-name ExampleSession \
--policy-arns '[{"arn":"arn:aws:iam::123456789012:policy/DenyS3GetObjectSession"}]'
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-session/test.txt" with an explicit deny in a session policy: arn:aws:iam::123456789012:policy/DenyS3GetObjectSession
セッションポリシーは S3 ユーザーガイドが ARN 表示の対象として挙げている種別で、ドキュメントどおりの動作でした。
アクセス許可境界
DenyS3GetObjectBoundary をカスタマー管理ポリシーとして作成し、ロール作成時にアクセス許可境界として設定しました。ロール側には S3 読み取り権限を付与し、拒否要因が境界のみになるようにしています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAll",
"Effect": "Allow",
"Action": "*",
"Resource": "*"
},
{
"Sid": "DenyS3GetObject",
"Effect": "Deny",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-boundary/*"
}
]
}
aws iam create-role \
--role-name ExampleRole \
--permissions-boundary arn:aws:iam::123456789012:policy/DenyS3GetObjectBoundary \
--assume-role-policy-document file://trust-policy.json
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-boundary/test.txt" with an explicit deny in a permissions boundary: arn:aws:iam::123456789012:policy/DenyS3GetObjectBoundary
いずれのケースでも、返るのはポリシーの ARN だけでした。どのステートメントが一致したのか、ポリシー本文がどうなっているのかは含まれません。
ARN が返らないケースは 2 通りに分かれます。バケットポリシーのように種別自体が ARN を持たない場合と、ARN を持つ種別をインラインで指定した場合です。いずれも次の節で扱います。
ARN が表示される条件は、ポリシーが ARN を持つかどうか
同じポリシー種別でも、ARN を持つマネージドポリシーとして指定したか、インラインで記述したかによって、表示が変わりました。
まずセッションポリシーです。前節と同じ内容のポリシーを、--policy-arns ではなく --policy にインライン JSON として渡しました。
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/ExampleRole \
--role-session-name ExampleSession \
--policy '{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":"*","Resource":"*"},{"Effect":"Deny","Action":"s3:GetObject","Resource":"arn:aws:s3:::amzn-s3-demo-bucket-session/*"}]}'
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-session/test.txt" with an explicit deny in a session policy
メッセージは session policy で終わり、ARN は付きませんでした。
アイデンティティベースポリシーでも同様です。put-role-policy でロールのインラインポリシーとして設定しました。
aws iam put-role-policy \
--role-name ExampleRole \
--policy-name DenyS3GetObjectInline \
--policy-document '{"Version":"2012-10-17","Statement":[{"Sid":"DenyS3GetObject","Effect":"Deny","Action":"s3:GetObject","Resource":"arn:aws:s3:::amzn-s3-demo-bucket-inline/*"}]}'
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-inline/test.txt" with an explicit deny in an identity-based policy
バケットポリシーも ARN を持たないため、種別だけが返ります。セッションポリシー検証で使ったバケットに次のポリシーを設定し、セッションポリシーを適用していない通常のロールで実行しました。拒否要因はバケットポリシーのみです。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyGetObject",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::amzn-s3-demo-bucket-session/*"
}
]
}
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-session/test.txt" with an explicit deny in a resource-based policy
マネージドポリシーかインラインかで表示が変わる点は、執筆時点で確認した S3 ユーザーガイドと IAM ユーザーガイドの該当ページには記載がなく、今回の実測で分かった挙動です。
今回試したインライン指定はセッションポリシーとアイデンティティベースポリシーの 2 ケースですが、いずれも ARN は表示されず、拒否原因の特定は従来どおり手作業になります。ロールに設定されたインラインポリシーを列挙し、該当するステートメントを目で探す作業が残るため、調査容易性を優先するなら Deny はカスタマー管理ポリシーとして持たせておくほうが有利です。
明示的 Deny・暗黙的 Deny・認証前の切り分け
403 が返っても、原因が明示的 Deny とは限りません。
同一 Organizations 内の別アカウントが所有するバケットへ、AWS CLI でアクセスしました。Allow が存在しないだけの暗黙的 Deny です。HTTP ステータスは 403 で、拒否理由は示されるもののポリシー ARN は付きませんでした。
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-other-account/example-object.yaml" because no resource-based policy allows the s3:GetObject action
次に、実在するプライベートバケットへ認証情報を付けずに HTTP リクエストを送りました。対象は認証ありの head-bucket で HTTP 200 を確認済みのバケットです。HTTP ステータスは 403 で、本文は汎用の Access Denied だけでした。
curl -s https://amzn-s3-demo-bucket-private.s3.ap-northeast-1.amazonaws.com/
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>...</RequestId><HostId>...</HostId></Error>
存在しないバケット名へ無認証でアクセスすると、HTTP 404 で NoSuchBucket が返りました。
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message><BucketName>amzn-s3-demo-bucket-no-such</BucketName>...</Error>
存在しないと想定してアクセスしたバケット名のうち 1 つでは、HTTP 403 で AllAccessDisabled が返りました。そのバケットは実在し、アクセスが無効化されている状態と考えられます。
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AllAccessDisabled</Code><Message>All access to this object has been disabled</Message>...</Error>
判定基準はプリンシパル名の有無です。User: arn:aws:sts:: から始まるメッセージは認可の評価まで到達しており、ポリシーを追う価値があります。ここまでの 4 例では暗黙的 Deny だけが該当し、残りは汎用の Access Denied、NoSuchBucket、AllAccessDisabled でした。ただし汎用の Access Denied は、原因が認証情報の不足なのかポリシーによる拒否なのかを文面から切り分けられません。
次の節では、無認証リクエストが RCP で拒否された場合も同じ汎用メッセージになることを確認します。
公開バケットでも無認証リクエストには ARN が出ない
ARN が表示される 5 種別のうち、組織外のプリンシパルへの適用が想定されるのは RCP です。RCP は組織内のリソースへのアクセスを、組織外のプリンシパルも含めて制御します。そこで公開バケットに RCP を適用し、無認証のリクエストへ ARN が露出しないかを確認しました。
パブリック公開したバケットに、denied.txt の s3:GetObject のみを Deny する RCP を適用しました。適用前は無認証で denied.txt と allowed.txt の両方が HTTP 200 で読めていました。
適用後、denied.txt への無認証リクエストは 403 になりました。
HTTP/1.1 403 Forbidden
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>...</RequestId><HostId>...</HostId></Error>
同じオブジェクトへ、同一組織内の認証済みプリンシパルからアクセスすると RCP の ARN が返りました。
An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::amzn-s3-demo-bucket-public/denied.txt" with an explicit deny in a resource control policy: arn:aws:organizations::111122223333:policy/o-exampleorgid/resource_control_policy/p-examplercpid2
対照として、同じバケットの allowed.txt は無認証のまま HTTP 200 で読めました。
HTTP/1.1 200 OK
this object is publicly readable
無認証リクエストへの応答本文には、ポリシー ARN は含まれませんでした。実測したのは無認証リクエストのみで、認証済みの組織外プリンシパルからのアクセスは未検証です。S3 ユーザーガイドは、同一組織外のクロスアカウントリクエストには汎用の Access Denied を返すと記載しています。
CloudTrail とサーバーアクセスログの違い
報告を受けてから調査する場合、エラーメッセージ全文が手元にないことがあります。CloudTrail と S3 サーバーアクセスログのどちらで事後にポリシー ARN を追えるかを確認しました。
CloudTrail の S3 データイベントには、errorMessage にエラーメッセージ全文が記録されます。ポリシー ARN もそのまま残ります。
{
"eventVersion": "1.11",
"eventName": "GetObject",
"errorCode": "AccessDenied",
"errorMessage": "User: arn:aws:sts::123456789012:assumed-role/ExampleRole/ExampleSession is not authorized to perform: s3:GetObject on resource: \"arn:aws:s3:::amzn-s3-demo-bucket-scp/test.txt\" with an explicit deny in a service control policy: arn:aws:organizations::111122223333:policy/o-exampleorgid/service_control_policy/p-examplescpid",
"additionalEventData": {
"httpStatusCode": 403
}
}
ただし、オブジェクト操作は管理イベントには記録されません。事後調査に利用するには、あらかじめデータイベントを有効化する必要があります。データイベントの記録には料金が発生する点にご注意ください。
S3 サーバーアクセスログについては、CloudWatch Logs への配信設定を作成し、create-delivery のレスポンスに含まれる recordFields を確認しました。
schema_version_id, bucket_arn, bucket_name, request_time, bucket_owner_id, remote_ip,
requester, request_id, operation, key_name, request_uri, http_status, error_code,
bytes_sent_size, object_size, total_duration, turn_around_duration, referer, user_agent,
version_id, host_id, signature_version, cipher_suite, authentication_type, host_header,
tls_version, access_point_arn, acl_required, source_region
http_status と error_code はありますが、拒否理由やポリシー ARN に相当するフィールドは存在しません。そのため、S3 サーバーアクセスログのみでは Deny の原因を特定できません。事後にポリシーまで追うには、CloudTrail のデータイベントが必要です。
まとめ
S3 の 403 Access Denied エラーに拒否したポリシーの ARN が含まれるようになり、明示的 Deny の原因調査が容易になりました。ARN を持つポリシーで Deny を管理していれば、エラーメッセージから該当ポリシーへ直接たどれます。
一方で、インラインポリシーやバケットポリシーのように ARN を持たないポリシーでは、従来どおり種別までしか分かりません。調査しやすさを重視するなら、Deny はカスタマー管理ポリシーとして管理することが有効です。
また、RCP によって公開バケットへの無認証リクエストを拒否した場合も、応答は汎用の Access Denied でした。組織外の無認証クライアントにポリシー ARN が露出する心配はありません。
今回の S3 対応は、SCP や RCP などによる明示的 Deny 時のエラーメッセージを改善するアップデートの一環です。詳細は、AWS Security Blog の記事をご覧ください。









