【问题标题】:Intermittent 403 CORS Errors (Access-Control-Allow-Origin) With Cloudfront Using Signed URLs To GET S3 ObjectsCloudfront 使用签名 URL 获取 S3 对象的间歇性 403 CORS 错误(访问控制允许来源)
【发布时间】:2019-07-03 20:48:59
【问题描述】:

简介

为了对我的多租户系统上的所有客户端保持上传的媒体(S3 对象)的私有性,我实施了 Cloudfront CDN 部署并将其(及其源 S3 存储桶)配置为强制按顺序使用签名 URL获取任何对象。


方法

首先,通过我的系统对用户进行身份验证,然后使用AWS JS SDK 提供的AWS.CloudFront.Signer.getSignedUrl() 方法生成签名的URL 并返回给他们。因此他们可以调用 CF/S3 来下载对象(图像、PDF、docx 等)。很标准的东西。


问题

上述方法在 95% 的时间内都有效。用户从我的系统中获取了一个签名的 URL,然后当他们使用 XHR 来获取对象时,它就被检索到了。

但是,有 5% 的时间会抛出 403 并带有 CORS 错误,指出客户端来源 is not allowed by Access-Control-Allow-Origin

此错误(错误)已在所有环境中得到确认:localhost、dev.myapp.com、prod.myapp.com。并且跨所有平台/浏览器。

它缺乏韵律或理由,以至于我实际上开始认为这是一个 AWS 错误(它们确实会发生,不时发生)。


到目前为止的调试清单

我这几天一直在想办法解决这个问题。到目前为止,这是我尝试过的:

您是否尝试过其他浏览器/平台?

是的。该问题存在于所有客户端来源、浏览器(和 版本)和所有平台。

您的 S3 存储桶是否为 CORS 正确配置?

是的。事实上,它是敞开的。我什至设置了<MaxAgeSeconds>0</MaxAgeSeconds> 为了防止缓存任何飞行前OPTIONS请求 客户:



签名的 URL 是否过期?

不。所有签名的 URL 都设置为在生成后 24 小时过期。这个问题甚至出现了几秒钟 在生成任何给定的签名 URL 之后。

生成签名 URL 的方法是否存在问题?

不太可能。我只是使用AWS.CloudFront.Signer.getSignedUrl() 他们的 JS SDK 的方法。签名的网址确实大部分时间都有效,所以 签名会是个问题,这似乎很奇怪 过程。此外,该错误显然是 CORS 错误,而不是签名 不匹配错误。

是时区/服务器时钟问题吗?

不。该系统确实为多个时区的用户提供服务,但是 鉴于签名的 URL 都是生成的,理论被证明是错误的 在服务器端。客户端的时区无关紧要,它得到 一个签名的 URL 从生成之日起 24 小时内有效,无论如何 TZ 进去了

您的 CF 发行版配置正确吗?

是的,据我跟踪几个AWS guidestutorials, docs and such.

为了简洁起见,这是一个屏幕截图。你可以看到我已经禁用 完全缓存以试图将其排除为原因:



您是否看到所有 mime 类型的错误?

没有。没有任何图像、音频或视频文件出现此错误 (对象)。已经完成了很多测试,这个错误似乎只是 尝试获取文档或 PDF 文件(.doc、.docx、 .pdf)。这让我相信这只是一个Accept 标头 不匹配错误:客户端正在发送带有标头的 XHR Accept: pdf,但实际上签名是为 Accept: application/pdf 生成的。 我还不能完全排除这种情况 原因。但鉴于错误是极不可能的 间歇性的。因此,如果它是 Accept 标头不匹配问题,那么它 每次都应该是一个错误。

此外,XHR 正在发送 Accept: */*,因此这不太可能是问题所在。



问题

我真的在这个问题上碰壁了。谁能看到我在这里缺少的东西?我能想到的最好的办法是这是某种“时间”问题。什么样的时间问题,或者甚至是时间问题,我还没有弄清楚。

提前感谢您的帮助。

【问题讨论】:

  • 如果 Access-Control-Allow-Origin 不允许来源 " 那么紧迫的问题似乎是 Access-Control-Allow-Origin 实际上包含什么? 和,还包括哪些其他响应标头?除了任何 CORS 标头之外,特别感兴趣的还有 AgeX-CacheServer
  • "客户端发送的 XHR 带有标头 Content-Type: pdf" ...这是一个令人困惑的声明,因为客户端不应该发送 any 带有GET 请求的Content-Type 标头。 Content-Type请求头指定请求体的MIME类型,GET请求没有请求体。
  • @AJB 你在这方面有什么进展吗?我遇到了完全相同的问题,除了我什至没有使用 CloudFront(服务器返回签名的 S3 url,浏览器对该 URL 执行 PUT)。它只发生大约 5-10% 的时间,而且我的 CORS 也是完全开放的。
  • 我们面临同样的问题。不是一个实际的解决方案,但使 Cloudfront 缓存无效可以解决问题。我们计划添加一个监控脚本,该脚本会经常检查这一点,并在出现故障时使缓存失效。
  • @AndresEspinosa 不幸的是,我没有。我确实尝试了下面 Abishak Garg 的回答中概述的解决方案,但它对我不起作用。不过,调查链接的 ServerFault 问题也是值得的。 (如果您找到可靠的解决方案,请发布!)

标签: amazon-web-services amazon-s3 amazon-cloudfront pre-signed-url


【解决方案1】:

在 serverfault 上找到了相同的解决方案。

https://serverfault.com/questions/856904/chrome-s3-cloudfront-no-access-control-allow-origin-header-on-initial-xhr-req

您显然无法成功地从 HTML 中获取对象,然后 使用 Chrome 和 S3 作为 CORS 请求再次成功获取它 (有或没有 CloudFront),由于在 实现。

添加原始帖子中的答案,以免丢失。

解决方法:

可以使用 CloudFront 和 Lambda@Edge 解决此行为,使用以下代码作为源响应触发器。

这会将 Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin 添加到来自 S3 的任何没有 Vary 标头的响应。否则,响应中的 Vary 头不会被修改。

'use strict';

// If the response lacks a Vary: header, fix it in a CloudFront Origin Response trigger.

exports.handler = (event, context, callback) => {
    const response = event.Records[0].cf.response;
    const headers = response.headers;

    if (!headers['vary'])
    {
        headers['vary'] = [
            { key: 'Vary', value: 'Access-Control-Request-Headers' },
            { key: 'Vary', value: 'Access-Control-Request-Method' },
            { key: 'Vary', value: 'Origin' },
        ];
    }
    callback(null, response);
};

【讨论】:

    猜你喜欢
    • 2019-11-03
    • 2020-08-16
    • 2017-12-30
    • 1970-01-01
    • 2017-12-28
    • 2014-08-06
    • 2022-01-09
    • 2017-01-10
    • 1970-01-01
    相关资源
    最近更新 更多