HTML5 Canvas getImageData 和同源策略

Posted

技术标签:

【中文标题】HTML5 Canvas getImageData 和同源策略【英文标题】:HTML5 Canvas getImageData and Same Origin Policy 【发布时间】:2011-06-08 01:01:27 【问题描述】:

我有一个在 pixie.strd6.com 上运行的站点和通过 Amazon S3 托管的图像,其 CNAME 为 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 @987654321@. Origin @987654322@ is not allowed by Access-Control-Allow-Origin.

理想情况下,HTML5 画布不会阻止从子域调用 getImageData。我研究过在 S3 中设置 Access-Control-Allow-Origin 标头,但没有成功。

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

【问题讨论】:

这个同源策略是有史以来最愚蠢的事情。如果我是一段恶意的 javascript 并且我想加载恶意数据,我只会在页面中包含任意脚本标签,而不是从图像数据中读取“s3kri7 c0mm4nd5”。唯一想要读取图像数据的人是客户端开发人员。至于从 *** 窃取“绝密图像数据”,如果您的网站已经被 xss 过,那么键盘记录将更具破坏性。所有这些“保护”的作用都是让合法的开发人员试图让 JavaScript 完成最简单的任务。 SOP 在这里防止合法的攻击媒介。假设您在照片共享网站上有一个私人相册(或检查存储在您的网上银行中的图像):如果没有脏画布保护,您访问的网络上的任何页面都可以获取那些图片如果他们知道 URL 并且您已登录,因为从 <img> 标签发送的请求使用您的 cookie。这里的问题不在于受到 XSS 攻击的站点;问题在于Web 上的任何页面 都可以使用您的身份验证 cookie 获取和读取画布上的图像。 tl;dr: 就目前而言,任何跨域网站都可以显示您需要授权的图片(私人照片、检查图片、等)在<img> 标记中,但是,由于 SOP,他们无法读取画布中这些图像的内容,例如,将它们保存到服务器。 我已经同意这确实可以防止一些合法的攻击媒介。现在随着 CORS 支持变得普遍,可以正确地允许访问托管在外部域的资源。对于普通开发人员来说,这仍然是一种痛苦,因为猫照片的额外安全性的成本/收益很低,但对于在图像中莫名其妙地显示敏感信息的银行来说,确保互联网的安全非常重要。 我想知道如果我喜欢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) 会发生什么。如果我在图像数据被污染之前获得对图像数据的引用,那么浏览器会做什么? 【参考方案1】:

Amazon recently announced CORS support

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

How to enable CORS

要配置您的存储桶以允许跨域请求,您需要创建一个 CORS 配置,这是一个 XML 文档,其中包含用于识别您将允许访问您的存储桶的来源的规则,操作(HTTP 方法)将支持每个来源,和其他特定于操作的信息。您最多可以向配置添加 100 条规则。您将 XML 文档作为 cors 子资源添加到存储桶中。

【讨论】:

【参考方案2】:

一种可能的解决方案是使用 nginx 作为代理。这里是如何配置去http://pixie.strd6.com/s3/的url传递到S3,但是浏览器仍然可以认为它是非跨域的。

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

【讨论】:

【参考方案3】:

如果您使用的是 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 可以是文件的获取参数

【讨论】:

【参考方案4】:

最近,我遇到了 Max Novakovic 的 $.getImageData。该页面包含几个关于 Flickr 照片获取和操作的简洁演示,以及一些代码示例。

它允许您从任意站点以 JavaScript 可操作的形式获取图像。它通过将脚本附加到页面来工作。然后该脚本从 Google App Engine 服务器请求图像。服务器获取请求的图像并将其转换为 base64 到脚本。当脚本接收到 base64 时,它会将数据传递给回调,然后回调可以将其绘制到画布上并开始处理它。

【讨论】:

【参考方案5】:

过去,Amazon S3 不允许您修改或添加 access-control-allow-origin 和 access-control-allow-credentials HTTP 标头,因此最好切换到 Rackspace Cloud 等其他服务文件或其他一些服务。

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

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

更多信息请参见http://www.w3.org/TR/cors/#use-cases。

使用允许您修改 HTTP 标头的服务完全解决了同源问题。

【讨论】:

【参考方案6】:

不使用 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'
 

“数据”是base64格式的图像数据。将其分配给图像。

img.src = result.data;

图像现在对于您的画布来说是“干净的”。

【讨论】:

【参考方案7】:

要编辑您的 S3 存储桶权限:

1) 登录 AWS 管理控制台并通过 https://console.aws.amazon.com/s3/ 打开 Amazon S3 控制台

2)在Buckets列表中,打开要查看属性的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'

【讨论】:

【参考方案8】:

此行为是设计使然。根据 HTML5 规范,一旦您将跨域图像绘制到画布上,它就会变脏,您将无法再读取像素。源匹配比较方案、完全合格的主机,在非 IE 浏览器中比较端口。

【讨论】:

是的,代理图像的服务器代码可以工作,但很遗憾我们不得不依赖一些不需要的东西。他们至少应该通过目标服务器上的 xml 文件实现跨域策略,就像使用 flash 所做的那样。【参考方案9】:

刚刚遇到同样的问题。我发现了可能有用的 CORS。

http://html5-demos.appspot.com/static/html5-whats-new/template/index.html#14

它对我不起作用,因为我试图从 Flickr 处理图像。所以,我仍在寻找解决方案。

【讨论】:

以上是关于HTML5 Canvas getImageData 和同源策略的主要内容,如果未能解决你的问题,请参考以下文章

HTML5中canvas图片加载的问题

canvas关于getImageData跨域问题解决方法

移动设备上糟糕的 Canvas GetImageData() / PutImageData() 性能

[js高手之路] html5 canvas系列教程 - 像素操作(反色,黑白,亮度,复古,蒙版,透明)

Canvas getImageData在某些移动设备上返回不正确的数据

html5 canvas系列教程-像素操作(反色,黑白,亮度,复古,蒙版,透明)