使用 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,问题应该会得到解决。
我正在尝试使用签名 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,问题应该会得到解决。