HTML5 Canvas getImageData 和同源策略

15

我有一个运行在 pixie.strd6.com 的网站,并通过 CNAME 映射到 Amazon S3 上的图像,路径为 images.pixie.strd6.com。

我想将这些图像绘制到 HTML5 画布上并调用 getImageData 方法,但是它会抛出 Error: SECURITY_ERR: DOM Exception 18

我尝试设置 window.domain = "pixie.strd6.com",但没有效果。

此外,$.get("http://dev.pixie.strd6.com/sprites/8516/thumb.png?1293830982", function(data) {console.log(data)})也会抛出错误:XMLHttpRequest cannot load http://dev.pixie.strd6.com/sprites/8516/thumb.png?1293830982. Origin http://pixie.strd6.com is not allowed by Access-Control-Allow-Origin.

理想情况下,HTML5 画布不应该阻止从子域名中调用 getImageData。我尝试在 S3 中设置 Access-Control-Allow-Origin header,但未成功。

非常感谢任何帮助或解决方法。


36
同源策略是最愚蠢的东西。如果我是一个恶意的 JavaScript 代码,想要加载恶意数据,我会在页面中包含一个任意的脚本标签,而不是从图片数据中读取"s3kri7 c0mm4nd5"。唯一想读取图像数据的是客户端开发人员。至于从 VPN 中窃取“绝密图像数据”,如果您的网站已经被 XSS 攻击,那么键盘记录将更具破坏性。所有这些所谓的“保护”只会激怒那些试图让 JavaScript 完成最简单任务的合法开发人员。 - Daniel X Moore
8
这里的SOP可以保护免受合法攻击。假设你在一个照片共享网站上拥有私人相册(或者检查存储在你的在线银行账户中的图像):如果没有脏画布保护,任何网页只要知道URL并且你已登录,就可以从<img>标签发送带有你的cookie的请求来获取这些图片。问题不在于被XSS攻击的网站被入侵了,而在于任何网页都可以使用你的身份验证cookie来获取和读取画布上的图片。 - apsillers
2
简而言之:目前,任何跨域站点都可以在<img>标签中显示您需要授权的图像(私人照片、支票图像等),但由于SOP的限制,它们无法在画布中读取这些图像的内容,例如将它们保存到服务器。 - apsillers
3
我已经开始认同这项措施可以防御某些合法的攻击方式。由于CORS支持越来越普及,现在可以正确地允许访问外部域名托管的资源。对于一般开发人员来说,这仍然很麻烦,因为为了保护猫照片的额外安全性成本与收益比很低,但重要的是互联网仍然要对银行保持安全,因为它们莫名其妙地将敏感信息显示在图像中。 - Daniel X Moore
我想知道如果我这样做 document.imd={}; 然后执行 document.imd[elementId] = document.getElementById(elementId).getContext("2d").getImageData(0,0,img.width, img.height);,并且只有在此之后才运行 document.getElementById(elementId).getContext("2d").drawImage(img,0,0),那么浏览器会怎么做,如果我在图像数据被污染之前获取了对它的引用? - Arioch 'The
9个回答

6

亚马逊最近宣布支持CORS(跨域资源共享)

我们很高兴地宣布Amazon S3现在支持CORS。 您现在可以轻松构建Web应用程序,使用JavaScript和HTML5与Amazon S3中的资源交互,从而使您能够实现HTML5拖放上传到Amazon S3、显示上传进度或更新内容等操作。 在此之前,您需要在Web应用程序和Amazon S3之间运行自定义代理服务器来支持这些功能。

如何启用CORS

要配置您的存储桶以允许跨域请求,您需要创建一个CORS配置,这是一个XML文档,其中包含一些规则来标识可以访问您存储桶的来源,每个来源所支持的操作(HTTP方法)以及其他与操作相关的信息。 您可以向配置中添加多达100个规则。 您将XML文档作为cors子资源添加到存储桶中即可完成配置。


4

一种可能的解决方案是使用nginx作为代理。以下是如何配置指向http://pixie.strd6.com/s3/的URL以通过S3,但浏览器仍然可以认为它是非跨域的。

location /s3/ {
  proxy_pass http://images.pixie.strd6.com/;
}

3
如果您正在使用PHP,可以这样做:

如果您正在使用PHP,可以这样做:

    function fileExists($path){
        return (@fopen($path,"r")==true);
    }
    $ext = explode('.','https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg');
    if(fileExists('https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg')){
        $contents = file_get_contents('https://cgdev-originals.s3.amazonaws.com/fp9emn.jpg');
        header('Content-type: image/'.end($ext));
        echo $contents;
    }

通过使用该php文件访问图片,例如如果文件名为generateImage.php,则可以这样做:<img src="http://GENERATEPHPLOCATION/generateImage.php"/>外部图片url可以是该文件的get参数。


2

过去,亚马逊S3不允许您修改或添加access-control-allow-origin和access-control-allow-credentials HTTP标头,因此最好切换到其他服务,如Rackspace Cloud Files或某些可以这样做的其他服务。

像这样添加或修改HTTP标头:

access-control-allow-origin: [your site]
access-control-allow-credentials: true

请参见http://www.w3.org/TR/cors/#use-cases了解更多信息。
使用允许您完全修改HTTP标头的服务可解决同源策略问题。

2
要编辑您的S3桶权限,请按照以下步骤进行:
1)登录AWS管理控制台,打开Amazon S3控制台,网址为https://console.aws.amazon.com/s3/ 2)在Bucket列表中,打开要查看属性的Bucket并单击“添加CORS配置”
3)在标签<CORSConfiguration>之间编写您想要添加的规则即可。
<CORSConfiguration>
  <CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
  </CORSRule>
</CORSConfiguration>

您可以在以下链接中了解更多关于规则的信息:http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html

4)在将要用于画布的图像上指定crossorigin ='anonymous'


2
最近,我发现了Max Novakovic的$.getImageData。该页面包括一些漂亮的示例,可以获取和操作Flickr照片,并提供了一些代码示例。
它允许您从任意站点以JavaScript可操作的形式获取图像。它通过向页面附加脚本来工作。然后,脚本从Google App Engine服务器请求图像。服务器获取所请求的图像并将其转换为base64格式传递给脚本。当脚本接收到base64时,它将数据传递给回调函数,然后可以将其绘制到画布上并开始处理。

2
对于不使用S3的用户,可以尝试构建一个图像代理,将图像文件编码并包装成JSON对象。
然后,您可以使用支持跨域的JSONP来获取JSON对象,并将图像数据分配给img.src。
我使用Google App Engine编写了一个图像代理服务器的示例代码。 https://github.com/flyakite/gae-image-proxy JSON对象返回的格式如下。
{ 
  'height': 50, 
  'width' : 50, 
  'data'  : 'data:image/jpeg;base64,QWRarjgk4546asd...QWAsdf'
} 

'data'是以base64格式编码的图像数据。将其分配给一个图像。
img.src = result.data;

图片现在已经“干净”,可以用于你的画布。

1

这种行为是按设计的。根据HTML5规范,一旦你将跨域图像绘制到画布上,它就变脏了,你就不能再读取像素了。源匹配比较方案、完全合格的主机,在非IE浏览器中还包括端口。


4
是的,服务器代码代理图像将起作用,但令人遗憾的是我们必须依赖于本不必要的东西。他们至少应该像Flash一样通过目标服务器上的XML文件实现跨域策略。 - Omiod

1

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