我使用 S3 网站终端节点作为我的 CloudFront 分配源。为什么我会收到“403 访问被拒绝”错误?

上次更新时间:2020 年 5 月 28 日

我使用 Amazon Simple Storage Service (Amazon S3) 存储桶作为我的 Amazon CloudFront 分配源。我使用 S3 静态网站终端节点作为源域名。为什么 CloudFront 从 Amazon S3 返回“403 访问被拒绝”错误?

简短描述

要解决“访问被拒绝”错误,您必须知道分配源的源域名是 S3 网站终端节点还是 S3 REST API 终端节点。按照以下步骤来确定终端节点类型:

  1. 打开 CloudFront 控制台
  2. 选择您的 CloudFront 分配,然后选择分配设置
  3. 选择源和源组选项卡。
  4. 查看源域名和路径下的域名,然后根据域名的格式确定终端节点类型。

REST API 终端节点使用以下格式:

AWSDOC-EXAMPLE-BUCKET.s3.amazonaws.com

注意:请务必遵循 Amazon S3 存储桶命名规则

网站终端节点使用以下格式:

AWSDOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com

如果您的分配使用 REST API 终端节点,请参阅我使用 S3 REST API 终端节点作为我的 CloudFront 分配源。为什么我会收到“403 访问被拒绝”错误?

如果您的分配使用的是网站终端节点,请确认是否符合以下要求,以免出现“访问被拒绝”错误:

  • 存储桶中的对象必须可公开访问。
  • 存储桶中的对象无法使用 AWS Key Management Service (AWS KMS) 加密。
  • 存储桶策略必须允许访问 s3:GetObject。
  • 如果存储桶策略授予公开访问权限,则拥有该存储桶的 AWS 账户还必须拥有该对象。
  • 请求的对象必须存在于存储桶中。
  • 必须禁用 Amazon S3 阻止公开访问。
  • 如果启用了申请方付款,则请求必须包含 request-payer 参数。

注意:如果您不想允许对 S3 对象进行公开(匿名)访问,请更改您的配置以将 S3 REST API 终端节点用作分配源。然后,将分配和 S3 存储桶配置为使用源访问身份 (OAI) 来限制访问权限。有关说明,请参阅如何使用 CloudFront 为 Amazon S3 上托管的静态网站服务?中的使用 REST API 终端节点作为源,并通过 OAI 限制访问权限

解决方法

存储桶中的对象必须可公开访问

使用网站终端节点的分配仅支持可公开访问的内容。如要确定您的 S3 存储桶中的对象是否可公开访问,请在 Web 浏览器中打开对象的 URL。或者,您也可以对 URL 运行 curl 命令。

下面是 S3 对象的示例 URL:

http://AWSDOC-EXAMPLE-BUCKET.s3-website-us-east-1.amazonaws.com/index.html

如果 Web 浏览器或 curl 命令返回“访问被拒绝”错误,则该对象不可公开访问。

允许通过下述方式之一公开访问对象:

存储桶中的对象无法进行 AWS KMS 加密

CloudFront 分配不支持 AWS KMS 加密对象。您必须从要使用分配提供服务的 S3 对象中删除 KMS 加密。

注意:使用 AES-256 加密您的对象,而不是使用 AWS KMS 加密。

使用下述方法之一来检查您存储桶中的对象是否为 KMS 加密对象:

  • 使用 Amazon S3 控制台查看对象属性。查看加密对话框。如果已选中 AWS-KMS,则对象已进行 KMS 加密。
  • 或者,您可以使用 AWS 命令行界面 (AWS CLI) 运行head-object 命令。如果命令返回的 ServerSideEncryptionaws:kms,则表明对象已进行 KMS 加密。

要使用 Amazon S3 控制台更改对象的加密设置,请参阅如何为 S3 对象添加加密?

要使用 AWS CLI 更改对象的加密设置,您必须先确认该对象的存储桶没有默认加密。如果存储桶没有默认加密,请运行以下 AWS CLI 命令,通过将对象复制到其本身来删除对象的加密。

警告:将对象复制到其本身将会删除 storage-classwebsite-redirect-location 的设置。要在新对象中保留这些设置,请务必明确在复制请求中指定 storage-classwebsite-redirect-location 的值。

aws s3 cp s3://AWSDOC-EXAMPLE-BUCKET/index.html s3://AWSDOC-EXAMPLE-BUCKET/index.html

存储桶策略必须允许访问 s3:GetObject

要使用采用 S3 网站终端节点的分配,您的存储桶策略必须不含阻止公开读取 s3:GetObject 操作的拒绝语句。

即使您的存储桶策略中有 s3:GetObject 的显式允许语句,也请确认没有冲突的显式拒绝语句。显式拒绝语句始终覆盖显式允许语句。

请按照以下步骤查看您的 s3:GetObject 存储桶策略:

1.    从 Amazon S3 控制台打开您的 S3 存储桶。

2.    选择权限选项卡。

3.    选择存储桶策略

4.    查看存储桶策略,查找包含 "Action": "s3:GetObject""Action": "s3:*" 的语句。

下面的示例策略包含适用于 s3:GetObject 公开访问的显式允许语句。但是,还有一个阻止访问 s3: GetObject 的显式拒绝语句,除非请求来自特定 Amazon Virtual Private Cloud (Amazon VPC)。

{
    "Version": "2008-10-17",
    "Id": "PolicyForCloudFrontPrivateContent",
    "Statement": [
        {
            "Sid": "Allow-OAI-Access-To-Bucket",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity EAF5XXXXXXXXX"
            },
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
            ]
        },
        {
            "Sid": "Allow-Public-Access-To-Bucket",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
            ]
        },
        {
            "Sid": "Access-to-specific-VPCE-only",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::AWSDOC-EXAMPLE-BUCKET/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:sourceVpce": "vpce-1a2b3c4d"
                }
            }
        }
    ]
  }

5.    修改存储桶策略,以删除或编辑阻止公开读取 s3: GetObject 的语句。

注意:CloudFront 可将“访问被拒绝”错误的结果缓存最多 5 分钟。从存储桶策略删除拒绝语句后,您可以在您的分配上运行失效以从缓存中删除对象。

如果存储桶策略授予公开访问权限,则拥有该存储桶的 AWS 账户还必须拥有该对象

对于允许公开访问对象的存储桶策略,拥有该存储桶的 AWS 账户还必须拥有该对象。存储桶或对象归创建该存储桶或对象的 AWS Identity and Access Management (IAM) 身份所属的账户所有。

注意:对象所有权要求适用于存储桶策略授予的公开访问权限。它不适用于对象的访问控制列表 (ACL) 授予的公开访问权限。

按照以下步骤检查存储桶和对象的拥有者是否相同:

1.    运行以下 AWS CLI 命令以获得存储桶拥有者的 S3 规范 ID:

aws s3api list-buckets --query Owner.ID

2.    运行以下命令以获得对象拥有者的 S3 规范 ID:

注意:此示例显示的是单个对象,但您可以使用此列表命令来检查多个对象。

aws s3api list-objects --bucket AWSDOC-EXAMPLE-BUCKET --prefix index.html

3.    如果规范 ID 不匹配,则该存储桶和对象的拥有者不同。

注意:您也可以使用 Amazon S3 控制台来检查存储桶和对象拥有者。拥有者可从相应存储桶或对象的权限选项卡中找到。

按照以下步骤将对象的拥有者更改为存储桶拥有者:

1.    通过对象拥有者的账户,运行以下命令以检索分配给对象的 ACL 权限:

aws s3api get-object-acl --bucket AWSDOC-EXAMPLE-BUCKET --key object-name

2.    如果对象具有 bucket-owner-full-control ACL 权限,则跳至步骤 3。如果对象没有 bucket-owner-full-control ACL 权限,则通过对象拥有者的账户运行以下命令:

aws s3api put-object-acl --bucket AWSDOC-EXAMPLE-BUCKET --key object-name --acl bucket-owner-full-control

3.    从存储桶拥有者的账户,运行此命令以通过将对象复制到其本身来更改对象的拥有者:

aws s3 cp s3://AWSDOC-EXAMPLE-BUCKET/index.html s3://AWSDOC-EXAMPLE-BUCKET/index.html

请求的对象必须存在于存储桶中

如果用户没有 s3:ListBucket 权限,则用户会因对象缺失遇到“访问被拒绝”错误,而不是“404 未找到”错误。运行 head-object AWS CLI 命令以检查存储桶中是否存在某对象。

注意:请确保发送到 CloudFront 的对象请求与 S3 对象名称完全匹配。S3 对象名称区分大小写。如果请求没有正确的对象名称,则 Amazon S3 响应就会像对象缺失一样。要确定 CloudFront 正在从 Amazon S3 请求哪个对象,可使用服务器访问日志记录

如果存储桶中存在该对象,则“访问被拒绝”错误不会屏蔽“404 未找到”错误。验证其他配置要求以解决“访问被拒绝”错误。

如果存储桶中不存在该对象,则“访问被拒绝”错误将屏蔽“404 未找到”错误。解决与对象缺失相关的问题。

注意:启用公开 s3: ListBucket 访问权限不是安全最佳实践。

必须禁用 Amazon S3 阻止公开访问

确认存储桶没有应用任何 Amazon S3 阻止公开访问设置。这些设置会覆盖允许公开访问的权限。阻止公开访问设置可以应用于各个存储桶或 AWS 账户。

如果启用了申请方付款,则请求必须包含 request-payer 参数

如果存储桶启用了申请方付款,则其他账户的用户在向存储桶发送请求时必须指定 request-payer 参数。否则,这些用户会遇到“访问被拒绝”错误。

对于 GET、HEAD 或 POST 请求,用户必须在标头中添加 x-amz-request-payer 参数。对于 REST 请求,用户必须在请求中添加 x-amz-request-payer 参数。