Cloudfront 使用签名 URL 获取 S3 对象的间歇性 403 CORS 错误(访问控制允许来源) [英] Intermittent 403 CORS Errors (Access-Control-Allow-Origin) With Cloudfront Using Signed URLs To GET S3 Objects

查看:22
本文介绍了Cloudfront 使用签名 URL 获取 S3 对象的间歇性 403 CORS 错误(访问控制允许来源)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

简要

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


方法

首先,用户通过我的系统进行身份验证,然后生成一个签名 URL 并使用

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

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


目前的调试清单

我这几天一直在想办法解决这个问题.这是我迄今为止尝试过的:

<块引用>

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

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

您的 S3 Bucket 是否为 CORS 配置正确?

是的.事实上,它是开放的.我什至设置了 0为了防止缓存任何飞行前 OPTIONS 请求客户:



签名网址是否已过期?

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

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

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

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

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

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

是的,就我可以通过以下几个

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

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

此外,XHR 正在发送 Accept: */* 所以这极不可能是问题所在.



问题

我真的遇到了这个问题.谁能看到我在这里缺少什么?我能想到的最好的办法是,这是某种时间"问题.什么样的时间问题,甚至是时间问题,我还没有弄清楚.

在此先感谢您的帮助.

解决方案

在 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';//如果响应缺少 Vary: 标头,请在 CloudFront 源响应触发器中修复它.export.handler = (事件、上下文、回调) =>{const response = event.Records[0].cf.response;const headers = response.headers;如果 (!headers['变化']){标题['变化'] = [{ key: 'Vary', value: 'Access-Control-Request-Headers' },{ key: 'Vary', value: 'Access-Control-Request-Method' },{键:'变化',值:'起源'},];}回调(空,响应);};

In Brief

In order to keep the uploaded media (S3 objects) private for all the clients on my multi-tenant system I implemented a Cloudfront CDN deployment and configured it (and its Origin S3 Bucket) to force the use of signed URLs in order to GET any of the objects.


The Method

First, the user is authenticated via my system, and then a signed URL is generated and returned to them using the AWS.CloudFront.Signer.getSignedUrl() method provided by the AWS JS SDK. so they can make the call to CF/S3 to download the object (image, PDF, docx, etc). Pretty standard stuff.


The Problem

The above method works 95% of the time. The user obtains a signed URL from my system and then when they make an XHR to GET the object it's retrieved just fine.

But, 5% of the time a 403 is thrown with a CORS error stating that the client origin is not allowed by Access-Control-Allow-Origin.

This bug (error) has been confirmed across all environments: localhost, dev.myapp.com, prod.myapp.com. And across all platforms/browsers.

There's such a lack of rhyme or reason to it that I'm actually starting to think this is an AWS bug (they do happen, from time-to-time).


The Debugging Checklist So Far

I've been going out of my mind for days now trying to figure this out. Here's what I've attempted so far:

Have you tried a different browser/platform?

Yes. The issue is present across all client origins, browsers (and versions), and all platforms.

Is your S3 Bucket configured for CORS correctly?

Yes. It's wide-open in fact. I've even set <MaxAgeSeconds>0</MaxAgeSeconds> in order to prevent cacheing of any pre-flight OPTIONS requests by the client:



Is the signed URL expired?

Nope. All of the signed URLs are set to expire 24hrs after generation. This problem has shown up even seconds after any given signed URL is generated.

Is there an issue with the method used to generate the signed URLs?

Unlikely. I'm simply using the AWS.CloudFront.Signer.getSignedUrl() method of their JS SDK. The signed URLs do work most of the time, so it would seem very strange that it would be an issue with the signing process. Also, the error is clearly a CORS error, not a signature mis-match error.

Is it a timezone/server clock issue?

Nope. The system does serve users across many timezones, but that theory proved to be false given that the signed URLs are all generated on the server-side. The timezone of the client doesn't matter, it gets a signed URL good for 24hrs from the time of generation no matter what TZ it's in.

Is your CF distro configured properly?

Yes, so far as I can make out by following several AWS guides, tutorials, docs and such.

Here's a screenshot for brevity. You can see that I've disabled cacheing entirely in an attempt to rule that out as a cause:



Are you seeing this error for all mime-types?

No. This error hasn't been seen for any images, audio, or video files (objects). With much testing already done, this error only seems to show up when attempting to GET a document or PDF file (.doc, .docx, .pdf). This lead me to believe that this was simply an Accept header mis-match error: The client was sending an XHR with the the header Accept: pdf, but really the signature was generated for Accept: application/pdf. I haven't yet been able to fully rule this out as a cause. But it's highly unlikely given that the errors are intermittent. So if it were a Accept header mis-match problem then it should be an error every time.

Also, the XHR is sending Accept: */* so it's highly unlikely this is where the issue is.



The Question

I've really hit a wall on this one. Can anyone see what I'm missing here? The best I can come up with is that this is some sort of "timing" issue. What sort of timing issue, or if it even is a timing issue, I've yet to figure out.

Thanks in advance for any help.

解决方案

Found the solution for the same on serverfault.

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

You apparently cannot successfully fetch an object from HTML and then successfully fetch it again with as a CORS request with Chrome and S3 (with or without CloudFront), due to peculiarities in the implementations.

Adding the answer from original post so that it does not get lost.

Workaround:

This behavior can be worked-around with CloudFront and Lambda@Edge, using the following code as an Origin Response trigger.

This adds Vary: Access-Control-Request-Headers, Access-Control-Request-Method, Origin to any response from S3 that has no Vary header. Otherwise, the Vary header in the response is not modified.

'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);
};

这篇关于Cloudfront 使用签名 URL 获取 S3 对象的间歇性 403 CORS 错误(访问控制允许来源)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆