使用 fetch 调用具有自定义域和签名 cookie 的 Cloudfront S3 端点时出现 403 错误

403 Error when using fetch to call Cloudfront S3 endpoint with custom domain and signed cookies

我正在尝试使用签名 cookie 通过 Cloudfront 为 S3 存储桶创建私有端点。我已经能够在 Lambda 中成功创建一个签名的 cookie 函数,它为我的根域添加了一个 cookie。

但是,当我为尝试访问的 S3 文件调用 Cloudfront 端点时,出现 403 错误。为了让事情变得更奇怪,我可以将 URL 复制并粘贴到浏览器中并可以访问该文件。

我们将调用我的根域 example.com。我的 cookie 域是 .example.com,我的开发应用程序 URL 是 test.app.example.com,我的 Cloudfront 端点 URL 是 tilesets.example.com

检查通话后,似乎没有发送 cookie。这很奇怪,因为我的 fetch 调用有 credentials: "include" 并且我正在调用 cookie 域的子域。

配置如下:

S3:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

云端:

不确定我在这里做错了什么。特别奇怪的是,当我直接进入浏览器中的 link 但当我获取时它不起作用,所以猜测这是一个 CORS 问题。

我一直在记录对 Cloudfront 的调用,如您所见,在我的主应用程序中使用 fetch 时未发送 cookie:

#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields

2019-09-13  22:38:40    IAD79-C3    369 <IP>    GET <CLOUDFRONT ID>.cloudfront.net  <PATH URL>/metadata.json    403 https://test.app.<ROOT DOMAIN>/ Mozilla/5.0%2520(Macintosh;%2520Intel%2520Mac%2520OS%2520X%252010_14_6)%2520AppleWebKit/537.36%2520(KHTML,%2520like%2520Gecko)%2520Chrome/76.0.3809.132%2520Safari/537.36   -   -   Error   5kPxZkH8n8dVO57quWHurLscLDyrOQ0L-M2e0q6X5MOe6K9Hr3wCwQ==    tilesets.<ROOT DOMAIN>  https   281 0.000   -   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 Error   HTTP/2.0    -   -

而当我直接在浏览器中转到 URL 时:

#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields

2019-09-13  22:32:38    IAD79-C1    250294  <IP>    GET <CLOUDFRONT ID>.cloudfront.net  <PATH URL>/metadata.json    200 -   Mozilla/5.0%2520(Macintosh;%2520Intel%2520Mac%2520OS%2520X%252010_14_6)%2520AppleWebKit/537.36%2520(KHTML,%2520like%2520Gecko)%2520Chrome/76.0.3809.132%2520Safari/537.36   -   CloudFront-Signature=<SIGNATURE>;%2520CloudFront-Key-Pair-Id=<KEY PAIR>;%2520CloudFront-Policy=<POLICY> Miss    gRkIRkKtVs3WIR-hI1fDSb_kTfwH_S2LsJhv9bmywxm_MhB7E7I8bw==    tilesets.<ROOT DOMAIN>  https   813 0.060   -   TLSv1.2 ECDHE-RSA-AES128-GCM-SHA256 Miss    HTTP/2.0    -   -

有什么想法吗?

您已正确诊断出问题是您的 cookie 没有被发送。

A cross-origin 请求不会包含带有 credentials: "include" 的 cookie,除非源服务器也在其响应中包含权限 headers:

Access-Control-Allow-Credentials: true

让 S3 允许这样做的方法并不明显,但我在 this answer.

中发现的线索后偶然发现了解决方案

修改存储桶的 CORS 配置以删除此:

<AllowedOrigin>*</AllowedOrigin>

...并添加此内容,具体列出您希望允许访问您的存储桶的来源(根据您的描述,这将是 parent 域):

<AllowedOrigin>https://example.com</AllowedOrigin>

(如果您需要 http,则需要单独列出,并且需要列出您需要允许使用 CORS 访问存储桶的每个域。)

这会将 S3 的行为更改为包含 Access-Control-Allow-Credentials: true。它似乎没有明确记录。

不要使用以下替代方案,即使它也可以工作,但不了解其中的含义。

<AllowedOrigin>https://*</AllowedOrigin>

这也会导致 Access-Control-Allow-Credentials: true,因此它会 "works" -- 但它允许来自任何地方的 cross-origin,这可能是您不希望的。话虽如此,请记住,CORS 只不过是一种仅适用于 well-behaved、non-malicious 网络浏览器的权限机制——因此将允许的来源设置设置为仅允许正确的域很重要,但它并不能神奇地保护您的内容免受来自其他地方未经授权的访问。我怀疑您已经意识到这一点,但请记住这一点很重要。

进行这些更改后,您需要清除浏览器缓存并使 CloudFront 缓存失效,并且 re-test。正确设置 CORS headers 后,您的浏览器应该会发送 cookie,问题应该会得到解决。