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

上次更新时间:2020 年 9 月 25 日

我使用 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 终端节点使用以下格式:

DOC-EXAMPLE-BUCKET.s3.amazonaws.com

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

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

DOC-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 参数。
  • 如果您使用 Referer 标头来限制从 CloudFront 访问 S3 源,请查看自定义标头。

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

解决方法

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

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

下面是 S3 对象的示例 URL:

http://DOC-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 控制台更改对象的加密设置,请参阅如何为 S3 对象添加加密?

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

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

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

存储桶策略必须允许访问 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:::DOC-EXAMPLE-BUCKET/*"
            ]
        },
        {
            "Sid": "Allow-Public-Access-To-Bucket",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
            ]
        },
        {
            "Sid": "Access-to-specific-VPCE-only",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": [
                "arn:aws:s3:::DOC-EXAMPLE-BUCKET/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:sourceVpce": "vpce-1a2b3c4d"
                }
            }
        }
    ]
  }

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

注意:CloudFront 会在错误缓存最小 TTL 指定的时间内缓存“访问被拒绝”错误的结果。默认值为 1 分钟。从存储桶策略删除 deny 语句后,您可以在您的分配上运行失效以从缓存中删除对象。

如果存储桶策略授予公开访问权限,则拥有该存储桶的 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 DOC-EXAMPLE-BUCKET --prefix index.html

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

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

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

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

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

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

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

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

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

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

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

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

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

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

注意:启用公开 s3: ListBucket 访问权限不是安全最佳实践。 启用公开 s3:ListBucket 访问权限会允许用户查看和列出存储桶中的所有对象。这将向用户公开对象元数据详细信息(例如,密钥和大小),即使用户没有下载相应对象的权限。

必须在存储桶上禁用 Amazon S3 阻止公开访问。

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

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

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

如果您使用 Referer 标头来限制从 CloudFront 访问 S3 源,请查看自定义标头。

如果您使用 Referer 标头限制从 CloudFront 访问 S3 网站终端节点源,请检查 S3 存储桶策略上设置的密钥值或令牌。然后,确认该密钥值或令牌与 CloudFront 源自定义标头上的值匹配。

如果您在存储桶策略中使用显式 deny 语句,则确认也存在基于 Referer 标头授予访问权限的 allow 语句。您不能仅使用显式 deny 语句授予访问权限。

例如,以下存储桶策略在请求中包含字符串 "aws:Referer":"MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER" 时授予对 S3 源的访问权:

{
  "Version":"2012-10-17",
  "Id":"http referer policy example",
  "Statement":[
    {
      "Sid":"Allow get requests originating from my CloudFront with referer header",
      "Effect":"Allow",
      "Principal":"*",
      "Action":"s3:GetObject",
      "Resource":"arn:aws:s3:::DOC-EXAMPLE-BUCKET/*",
      "Condition":{
        "StringLike":{"aws:Referer":"MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER"}
      }
    }
  ]
}
使用此示例存储桶策略时,CloudFront 源自定义标头必须为:

  • 标头:Referer
  • 值:MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER

注意:由于委托人是通配符值 ("Principal":"*"),示例存储桶策略将授予对存储桶的公共(匿名)访问权。但由于条件语句,仅在请求中包含 Referer 标头且标头值与存储桶策略中的值匹配时授予对 S3 源的访问权。