加载 AWS CloudFront 文件时出现 403(禁止)

Posted

技术标签:

【中文标题】加载 AWS CloudFront 文件时出现 403(禁止)【英文标题】:Getting 403 (Forbidden) when loading AWS CloudFront file 【发布时间】:2016-04-25 05:47:25 【问题描述】:

我正在开发一个视频应用程序并将文件存储在 AWS S3 上,使用像 https://***.amazonaws.com/*** 这样的默认 URL 可以正常工作,但我决定使用更快的内容交付 CloudFront。

使用 CF,我不断使用此 URL https://***.cloudfront.net/*** 获取 403 (Forbidden)。我错过了什么吗?

在我决定从指向我的存储桶的 CloudFront 加载内容之前,一切正常。

请问有什么解决办法吗?

【问题讨论】:

你没有给我们太多的帮助。您是否使用预签名 URL?您的存储桶策略是否根据某些请求参数拒绝请求? @Michael-sqlbot 我没有使用预签名的 URL,只是标准配置。我设置的策略是只接受我的 URL 来加载文件。 那么,您正在使用带有 "Condition": "StringLike":"aws:Referer":["http://www.example.com/*"] 之类的存储桶策略? @Michael-sqlbot 没错,甚至只是为了测试而删除策略也无济于事。我有点迷茫 如果我没看错,请注意,您现在可以使用 WAF 而不是使用 S3 方法在 CloudFront 上进行Referer 检查。我已经报道了这个here。 (我还将更新我的帖子以提及@Michael-sqlbot 的答案,这很整洁) 【参考方案1】:

使用检查传入 Referer: 标头的存储桶策略限制对 S3 内容的访问时,您需要进行一些自定义配置以“智取”CloudFront。

了解 CloudFront 被设计为行为良好的缓存非常重要。我所说的“行为良好”是指 CloudFront 旨在永远不会返回与原始服务器返回的响应不同的响应。我相信您可以看到这是一个重要因素。

假设我在 CloudFront 后面有一个 Web 服务器(不是 S3),我的网站经过设计,可以根据检查 Referer: 标头...或任何其他 http 请求标头返回不同的内容,例如User-Agent: 例如。根据您的浏览器,我可能会返回不同的内容。 CloudFront 如何知道这一点,从而避免为用户提供某个页面的错误版本?

答案是,它无法分辨——它无法知道这一点。因此,CloudFront 的解决方案是根本不将大多数请求标头转发到我的服务器。我的 Web 服务器看不到,它无法做出反应,因此我返回的内容不会因我未收到的标头而异,这会阻止 CloudFront 根据这些标头缓存和返回错误的响应。 Web 缓存有义务避免为给定页面返回错误的缓存内容。

“但是等等,”你反对。 “我的网站依赖于某个标头的值以确定如何响应。”对,这是有道理的……所以我们必须告诉 CloudFront:

我需要您转发Referer:User-Agent: 或浏览器发送的其他几个标头之一,而不是仅根据请求的路径缓存我的页面,并缓存响应以供使用在其他请求上,这些请求不仅包含相同的路径,而且您转发给我的额外标头的值也相同

但是,当源服务器是 S3 时,CloudFront 不支持转发大多数请求标头,假设由于静态内容不太可能发生变化,这些标头只会导致它不必要地缓存多个相同的响应。

您的解决方案是不要告诉 CloudFront 您使用 S3 作为源。相反,将您的分配配置为使用“自定义”源,并为其提供存储桶的主机名以用作源服务器主机名。

然后,您可以将 CloudFront 配置为将 Referer: 标头转发到源,并且您的基于该标头拒绝/允许请求的 S3 存储桶策略将按预期工作。

嗯,几乎和预期的一样。这将在一定程度上降低您的缓存命中率,因为现在缓存的页面将基于路径+引用页面进行缓存。如果您的站点的多个页面引用了 S3 对象,CloudFront 将为每个唯一请求缓存一个副本。这听起来像是一个限制,但实际上,它只是正确缓存行为的产物——无论转发到后端,几乎所有这些,都必须用于确定该特定响应是否可用于服务未来的请求。

请参阅http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesForwardHeaders,了解如何将 CloudFront 配置为将特定标头列入白名单以发送到您的源服务器。

重要提示:不要转发任何您不需要的标头,因为每个变体请求都会进一步降低您的命中率。特别是在使用 S3 作为自定义源的后端时,不要转发 Host: 标头,因为这可能不会达到您的预期。在此处选择Referer: 标头,然后进行测试。 S3 应该开始看到标题并做出相应的反应。

请注意,当您删除存储桶策略进行测试时,CloudFront 将继续提供缓存的错误页面,除非您通过发送失效请求刷新缓存,这会导致 CloudFront 清除与您指定的路径模式匹配的所有缓存页面,在大约 15 分钟的过程中。试验时最简单的做法是使用新配置创建一个新的 CloudFront 分配,因为分配本身不收取任何费用。

查看来自 CloudFront 的响应标头时,请注意 X-Cache:(命中/未命中)和 Age:(此特定页面被缓存多久之前)响应。这些在故障排除中也很有用。


更新: @alexjs 做了一个重要的观察:而不是使用存储桶策略来执行此操作并将 Referer: 标头转发到 S3 进行分析 - 这会损害您的缓存比率范围随着资源在引用页面上的分布而变化——您可以使用新的 AWS Web 应用程序防火墙服务,该服务允许您对传入 CloudFront 的请求施加过滤规则,以允许或阻止基于 string matching in request headers 的请求。

为此,您需要将分发连接到 S3 作为 S3 源(正常配置,与我在上面的解决方案中提出的相反,使用“自定义”源)并使用内置功能CloudFront 对 S3 的后端请求进行身份验证(因此,如果恶意行为者直接从 S3 请求,则无法直接访问存储桶内容)。

有关此选项的更多信息,请参阅https://www.alexjs.eu/preventing-hotlinking-using-cloudfront-waf-and-referer-checking/。

【讨论】:

【参考方案2】:

另外,它可能很简单。当您首次将文件上传到 S3 存储桶时,它是非公开的,即使该存储桶中的其他文件是公开的,即使存储桶本身是公开的。

要在 AWS 控制台中更改此设置,请选中您要公开的文件夹(您刚刚上传的文件夹)旁边的框,然后从菜单中选择“公开”。

该文件夹(以及任何子文件夹)中的文件将被公开,您将能够从 S3 提供文件。

对于 AWS CLI,在命令中添加“--acl public-read”选项,如下所示:

aws s3 cp index.html s3://your.remote.bucket --acl public-read

【讨论】:

使用cloudfront访问S3时,应该使用源访问ID,而不是将S3存储桶暴露给公众。然后存储桶可以授予对存储桶策略的权限(如果使用控制台设置云端,这实际上可以自动完成)。 你说得对,这是 Cloudfront 的首选方式(我个人使用的方式)。我想我的回答更多的是提醒您,即使您将存储桶标记为公开,您也需要将每个单独的文件标记为公开。【参考方案3】:

对我来说,我必须让 CodePipeline 访问我的 S3 存储桶策略。例如这样的:


    "Version": "2012-10-17",
    "Statement": [
        
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::mys3bucket/*"
        
    ]

【讨论】:

是的,将主体更改为 * 可能是我们通常将其配置为只能从云端访问的大多数情况的解决方案【参考方案4】:

我确定了 CloudFront 可以返回 403 (Bad request) 的另一个原因。也许这是一个边缘案例,但我想与您分享。

CloudFront 实施了转发环路检测机制来防止转发环路攻击。 根据 AWS 支持,您不能将超过 2 个 CloudFront 分配级联为源。

假设您已将 CloudFront A 配置为 CloudFront B 作为源,并且从 CloudFront B 将 CloudFront C 配置为源,并且从 CloudFront C 将 S3 存储桶作为源。

A --> B --> C --> S3 bucket (can return a 403 error)

如果您从位于级联末端的 S3 存储桶中的 CloudFront A 请求文件,CloudFront C 将返回 403(错误请求)。

如果您的级联仅包含 2 个 CloudFront 分配和最后一个 S3 存储桶,则来自 S3 源的文件请求有效。

A --> B --> S3 bucket (works)

【讨论】:

【参考方案5】:

我的要求是使存储桶私有,所以我使用了 OAI,我面临的主要问题是我在创建分发之前创建了 OAI,并在源部分下拉列表中选择它,云端开始向我抛出 403。我通过让云端创建 OAI 而解决了这个问题创建云端来源(我从下拉列表中选择了来源域名并选择了存储桶,然后它提供了限制 s3 存储桶的选项,然后您将获得创建原始访问身份的选项和另一个名为授予对存储桶的读取权限的选项,让 aws/cloudfront处理它)

有时aws可能无法在s3存储桶中添加OAI权限,请使用此文档手动添加权限

https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html#private-content-granting-permissions-to-oai

还要确保您在 s3 和 cloudfront 中都给出了入口点(在我的例子中是 index.html)

我没有在云端创建任何错误页面,希望它能节省别人的时间

编辑:重新加载页面抛出 403 错误,所以我在云端添加了 403 和 404 的错误页面和页面为“/index.html”

【讨论】:

重新加载页面并重定向到 index.html 是这里最大的问题。如果开发人员来自任何使用 gatsby 或 pre-rendered.io 的预渲染应用程序,您可以尝试以下方法。确保将源指向 s3 存储桶网站端点,并更新存储桶策略以使 Principal 为“*”。这适用于整个应用程序是静态内容并且您为仅客户端路由使用不同存储桶的情况。【参考方案6】:

对于 POST 请求,我从云端收到 403 错误,我的来源是域名而不是 s3 存储桶。

原因是 POST 默认情况下 cloudfront 不允许使用。我从控制台的Behaviors 选项卡中启用了POST,然后它就起作用了。

【讨论】:

【参考方案7】:

一个问题可能是您没有指定 CNAME(特定的或通配符),当您尝试使用域名时,它不起作用,但可以使用 CF Distro url

【讨论】:

【参考方案8】:

我遇到了类似的问题, 但在我的存储桶策略中,我在资源部分只提到了存储桶 ARN。 而不是我需要提及 bucketname/* 以允许访问该存储桶中的所有对象。 认为这可能对一些面临类似问题的人有所帮助。

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。【参考方案9】:

我通过更新我的云端分发下的原始域来解决

在源选项卡下编辑源名称不要直接从列表中选择存储桶名称,而是从您的 s3 存储桶中复制静态网站托管(在属性选项卡下检查)

test.uk.s3-website.eu-west-2.amazonaws.com

【讨论】:

以上是关于加载 AWS CloudFront 文件时出现 403(禁止)的主要内容,如果未能解决你的问题,请参考以下文章

创建 CloudFront 分配时出现神秘的 CloudFormation 失败

如何为外部字体请求设置 AWS Cloudfront 缓存?

AWS Toolkit for Eclipse Mars 在启动时出现错误

将 CloudFront 分配与 Route 53 域相关联时出现问题

使用 SNI 通过 HTTPS 提供服务时出现 CloudFront 错误

尝试在 AWS EMR 中获得 GPU 支持时出现错误“一个 NVIDIA 内核模块 'nvidia' 似乎已加载到您的内核中”