缓存图片的CORS策略

63

在Chrome 22和Safari 6中。

使用启用CORS的S3存储桶加载图像以在画布中使用(并进行提取),以下是代码:

<!-- In the html -->
<img src="http://s3....../bob.jpg" /> 

// In the javascript, executed after the dom is rendered
this.img = new Image();
this.img.crossOrigin = 'anonymous';
this.img.src = "http://s3....../bob.jpg";

我观察到以下情况:

  1. 禁用缓存
  2. 一切正常,两个图片都加载

然后启用缓存:

  1. 启用缓存
  2. DOM 图片加载成功,canvas 图片创建时出现 DOM 安全异常

如果我修改 JavaScript 代码,添加查询字符串:

this.img = new Image();
this.img.crossOrigin = 'anonymous';
this.img.src = "http://s3....../bob.jpg?_";

即使启用了缓存,一切都能正常工作。我通过使用HTTP代理并观察在失败情况下实际上没有从服务器请求图像来得出缓存是一个问题。

我被迫得出的结论是,图像缓存保存了原始请求头,然后将其用于后续的CORS启用请求,并且由于违反了同源策略而生成了安全异常。

这是否是预期行为?

编辑:在火狐浏览器中运行。

编辑2:S3存储桶上的CORS策略。

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

我现在正在使用 wide open,因为我只是从我的本地计算机进行测试。这还没有进入生产阶段。

编辑3:更新了跨源资源共享策略以指定来源。

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
    <CORSRule>
        <AllowedOrigin>http://localhost:5000</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
    </CORSRule>
</CORSConfiguration>

已验证的出站标头:

Origin  http://localhost:5000
Accept  */*
Referer http://localhost:5000/builder
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
Accept-Charset  ISO-8859-1,utf-8;q=0.7,*;q=0.3

传入的标头:

Access-Control-Allow-Origin http://localhost:5000
Access-Control-Allow-Methods    GET
Access-Control-Allow-Credentials    true

如果我在加载到画布中时不清除缓存,仍然无法在Chrome中成功执行。

编辑4:

刚注意到在失败的情况下出现了这个问题。

传出的标头:

GET /373c88b12c7ba7c513081c333d914e8cbd2cf318b713d5fb993ec1e7 HTTP/1.1
Host    amir.s3.amazonaws.com
User-Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.91 Safari/537.4
Accept  */*
Referer http://localhost:5000/builder
Accept-Encoding gzip,deflate,sdch
Accept-Language en-US,en;q=0.8
Accept-Charset  ISO-8859-1,utf-8;q=0.7,*;q=0.3
If-None-Match   "99c958e2196c60aa8db385b4be562a92"
If-Modified-Since   Sat, 29 Sep 2012 13:53:34 GMT

传入的请求头:

HTTP/1.1 304 Not Modified
x-amz-id-2  3bzllzox/vZPGSn45Y21/vh1Gm/GiCEoIWdDxbhlfXAD7kWIhMKqiSEVG/Q5HqQi
x-amz-request-id    48DBC4559B5B840D
Date    Sat, 29 Sep 2012 13:55:21 GMT
Last-Modified   Sat, 29 Sep 2012 13:53:34 GMT
ETag    "99c958e2196c60aa8db385b4be562a92"
Server  AmazonS3

认为这是由dom触发的第一个请求。虽然我不确定它是否是JavaScript请求。


你尝试过使用 Access-Control-Allow-Origin * 吗?因为 http://localhost:5000/ 可能会误导。 - Borislav Sabev
1
嗨!你解决了吗?谢谢! - chemitaxis
你是一个很棒的人!谢谢你发布这个 - 我苦苦挣扎了好几个小时。 - Chris Scott
1
像趋化性一样,我不知道解决方案是什么。你能告诉我们这是如何解决的吗? - Jared Martin
我花了很多天来调试这个程序。谢谢! - Aanchal1103
显示剩余2条评论
3个回答

16
问题在于图片没有必要的CORS头信息,导致浏览器从以前的请求缓存中获取图像。因此,当你再次请求带有“crossorigin”属性的canvas时,浏览器使用缓存版本,无法看到必要的头信息,从而引发CORS错误。 当你在url后添加“?_”时,浏览器会忽略缓存,因为这是另一个URL。 参考这个线程: https://bugs.chromium.org/p/chromium/issues/detail?id=409090 Firefox和其他浏览器没有这个问题。

如果我们想要使用缓存,那么如何使用CORS头缓存图像? - Mayur Kukadiya
1
但是这个解决方案不能与预签名的URL一起使用。 - Matteo
实际上,如果文件服务器缓存配置为在缓存键中包含查询参数,则任何参数都可以起作用。想法是为加载了没有crossorigin=anonymous的图像和加载了crossorigin=anonymous的图像分别设置不同的URL。这样它们就不会“共享”相同的缓存。例如,您可以向要使用cors标头缓存的图像URL添加参数?crossorigin(或?tartiflette)。 - Hugo Mallet

8
描述的行为似乎是合理的,因为缓存条目键是目标URI(请参见7234超文本传输协议(HTTP/1.1):缓存)。要解决此问题并有效使用缓存,您需要使图像托管服务器在两种情况下给出相同的响应。其中一种选择是在第一次请求中也发送Origin HTTP头(假设具有键targetUri的响应尚未在缓存中):
<img src="targetUri" crossorigin="anonymous" />

另一种选择是将图像托管服务器配置为发送CORS相关的HTTP头,而不管请求是否包含Origin HTTP标头。有关更多信息,请参见StackOverflow上S3 CORS, always send Vary: Origin的讨论。

此外,您可以使用Vary响应HTTP标头,通知用户代理响应对Origin请求HTTP标头敏感。缺点是可能用户代理仅将Vary标头用作响应验证器(而不是缓存条目键的一部分),并且仅为目标URI存储单个响应实例,这使得有效使用缓存变得更加困难。有关更多信息,请查看Mark Nottingham的文章The State of Browser Caching, Revisited


请问您能否解释一下如何在使用img标签加载图像时,带有CORS头缓存图像。 - Mayur Kukadiya
在负责第一个图像的GET请求的<img>标签中添加属性crossorigin="anonymous"。 - Hugo Mallet

0
你应用了什么CORS设置?这篇文章建议在AllowedOrigin中使用通配符会被解析(而不是直接发送,这似乎是未记录的行为);然后Access-Control-Allow-Origin头部值会被缓存以供后续请求使用,导致类似于你报告的问题。

我添加了我的CORS策略。你是说客户端会缓存Access-Control-Allow-Origin头吗?我将尝试使用本地主机作为我的来源。 - amirpc
即使指定了AllowedOrigin的域名,我仍然看到这种行为。 - amirpc
刚刚再次验证了一下,如果我清空缓存,一切都正常运作。如果我不清空缓存,无论我的来源是通配符还是具体的,都无济于事。我测试了Chrome、Safari和Firefox。Firefox在所有情况下都能正常工作,而Chrome/Safari似乎存在这个 bug。肯定是Webkit问题。 - amirpc
@amirpc:不,我认为你的问题可能是由CloudFront或代理缓存引起的。现在,你指责Chrome,我认为这个问题可能更相关于你的情况 - Oleg
我没有看到预检请求,我正在使用HTTP代理并查看,没有选项请求通过。只有添加If-None-Match标头的获取。获取在我的CORS策略中,并且应该工作。但是,这些获取不指定来源,可能是因为缓存版本没有来自DOM请求。 - amirpc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接