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

上次更新日期:2022 年 10 月 12 日

我使用 Amazon Simple Storage Service(Amazon S3)存储桶作为我的 Amazon CloudFront 分配源。如何排查“403 访问被拒绝”错误?

简短描述

要解决“访问被拒绝”错误,请先确定分配源的源域名是 S3 网站终端节点还是 S3 REST API 终端节点。然后,如果您的分配使用的是网站终端节点,请查看问题排查部分。

解决方法

确定您的分配源域名的端点类型

1.    打开 CloudFront 控制台

2.    选择您的 CloudFront 分配。然后,选择 Distribution Settings(分配设置)。

3.    选择源和源组选项卡。

4.    查看 Origin Domain Name and Path(源域名和路径)下的域名。根据域名的格式确定终端节点类型:

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

DOC-EXAMPLE-BUCKET.s3.amazonaws.com

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

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

注意:根据 AWS 区域的不同,终端节点格式可能使用破折号形式(s3-website-Region)或点形式(s3-website.Region)。如果您的分配使用 REST API 终端节点,请参阅我使用 S3 REST API 终端节点作为我的 CloudFront 分发源。为什么我会收到“403 访问被拒绝”错误?

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

确认存储桶中的对象未被 AWS Key Management Service (AWS KMS) 加密

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

确定对象是否采用 AWS KMS 加密

要检查存储桶中的对象是否为 KMS 加密:

使用 Amazon S3 控制台查看对象属性。查看 Encryption(加密)对话框。如果已选中 AWS-KMS,则对象已进行 AWS KMS 加密。

-或者-

使用 AWS 命令行界面(AWS CLI)运行 head-object 命令。如果命令返回的 ServerSideEncryptionaws:kms,则表明对象已进行 AWS KMS 加密。

注意:如果在运行 AWS CLI 命令时遇到错误,请确保您使用的是最新版本的 AWS CLI

更改对象的加密设置

要使用 Amazon S3 控制台更改对象的加密设置,请参阅使用 AWS KMS 指定服务器端加密(SSE-KMS)

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

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

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

确认存储桶策略中没有针对 s3:GetObject 操作的显式“拒绝”

您的存储桶策略必须不含阻止公开读取 s3:GetObject 操作的拒绝语句。

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

要查看 s3:GetObject 的存储桶策略:

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

2.    选择权限选项卡。

3.    选择存储桶策略

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

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

例如,下面的示例策略包含适用于 s3:GetObject 公开访问的显式允许语句。但是,它还包含 s3:GetObject 访问的显式拒绝语句,除非请求来自特定 Amazon Virtual Private Cloud(Amazon VPC)。必须修改此策略才能允许 s3:GetObject 操作。

{
  "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"
        }
      }
    }
  ]
}

如果存储桶策略授予公开读取访问权限,则确认拥有该存储桶的 AWS 账户也拥有该对象

对于允许公开读取访问对象的存储桶策略,拥有该存储桶的 AWS 账户还必须拥有该对象。对于具有默认对象所有权设置的现有 Amazon S3 存储桶,对象拥有者是将对象上载到存储桶的 AWS Identity and Access Management (IAM) 身份的 AWS 账户。

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

确认存储桶和对象拥有相同的拥有者

使用以下步骤检查存储桶和对象的拥有者是否相同。您也可以使用 Amazon S3 控制台来检查存储桶和对象拥有者。拥有者可从相应存储桶或对象的 Permissions(权限)选项卡中找到。

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 不匹配,则该存储桶和对象的拥有者不同。

更新对象所有权

存储桶拥有者可以使用 S3 对象所有权来管理对象的所有权。所有新的 S3 存储桶都默认开启了存储桶拥有者强制设置。要更新现有存储桶,请参阅设置现有存储桶的对象所有权。开启存储桶拥有者强制设置后,存储桶拥有者将成为存储桶内所有对象的对象拥有者。此外,当开启存储桶拥有者强制设置时,存储桶及其对象上的所有 ACL 都将被停用。

最佳实践是,存储桶拥有者在所有存储桶上使用存储桶拥有者强制设置,并通过 IAM 和存储桶策略管理权限。

要删除存储桶的 ACL 并获取存储桶中所有对象的所有权,请运行以下命令:

aws s3api put-bucket-ownership-controls --bucket example-bucket --ownership-controls 'Rules=[{ObjectOwnership=BucketOwnerEnforced}]'

如果您不想在 S3 存储桶上关闭 ACL,也可以按照以下步骤将对象的拥有者更改为存储桶拥有者:

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 权限,则通过对象拥有者的账户运行以下命令。请将 DOC-EXAMPLE-BUCKET 替换为您的存储桶名称。

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

3.    从存储桶拥有者的账户,运行以下命令以通过将对象复制到其本身来更改对象的拥有者。请将 DOC-EXAMPLE-BUCKET 替换为您的存储桶名称。

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 请求哪个对象,可使用服务器访问日志记录

如果您收到有关默认根对象的错误,请确保您的对象名称没有任何额外字符。例如,应在 Default Root Object(默认根对象)字段中输入 index.html,而不是 /index.html。有关更多信息,请参阅如何指定默认根对象

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

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

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

确认已关闭存储桶的 Amazon S3 阻止公开访问

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

确认存储桶中的对象可公开访问

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

例如:

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

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

要允许公开读取访问权限:

创建允许对存储桶中的所有对象进行公开读取访问的存储桶策略。

-或者-

使用 Amazon S3 控制台允许对该对象进行公开读取访问

如果已开启申请方付款,请确认请求中包含 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 源的访问权。
CloudFront 源自定义标头必须是:

  • Header(标头):Referer
  • Value(值):MY_SECRET_TOKEN_CONFIGURED_ON_CLOUDFRONT_ORIGIN_CUSTOM_HEADER
{
  "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"}
      }
    }
  ]
}

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

确认贵企业的管理账户没有附加明确的“拒绝”服务控制策略(SCP)

服务控制策略(SCP)是一种企业策略,您可以在企业中用它来管理权限。使用贵企业在 AWS Organizations 中的管理账户检查是否有拒绝策略(s3:GetObject 操作)附加到到企业根、企业单位(OU)或直接附加到您的 AWS 账户。