使用检查传入 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/。