如何修复getImageData()错误画布已被跨域数据污染?


131

我的代码在localhost上运行良好,但在站点上不运行。

我从控制台收到此行的错误.getImageData(x,y,1,1).data

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. 

我的代码的一部分:

jQuery.Event.prototype.rgb=function(){
        var x =  this.offsetX || (this.pageX - $(this.target).offset().left),y =  this.offsetY || (this.pageY - $(this.target).offset().top);
        if (this.target.nodeName!=="CANVAS")return null;
        return this.target.getContext('2d').getImageData(x,y,1,1).data;
    }

注意:我的图片网址(src)来自子域网址


8
即使img.src是本地相对URL,我也遇到此错误:“ img / foo.png”-那么这是跨域的吗?
桑福德·斯塔布

请参阅stackoverflow.com/a/16218015/5175433:“Chrome浏览器不认为不同的本地文件来自同一域。”
A_P

Answers:


116

正如其他人所说,您是通过从跨起源域加载来“污染”画布。

https://developer.mozilla.org/zh-CN/docs/HTML/CORS_Enabled_Image

但是,您可以通过简单的设置来防止这种情况:

img.crossOrigin = "Anonymous";

仅当远程服务器适当设置以下标头时,此方法才有效:

Access-Control-Allow-Origin "*"

使用“直接链接”选项时,Dropbox文件选择器就是一个很好的例子。我在oddprints.com上使用它来将图像从远程保管箱图像URL 悬停到我的画布中,然后将图像数据提交回我的服务器。全部用JavaScript


1
当将imgur图像加载到jsfiddle.net时,此方法可以解决此错误
Travis

3
如果我先输入交叉源,然后设置源,然后访问数据加载,对我有用
琼斯

2
如果它是本地文件,而您的应用程序根本不使用互联网怎么办?就像android webview应用程序一样,图像与html文件位于同一文件夹中。这不能解决。
柯蒂斯,

44

我发现我必须使用.setAttribute('crossOrigin', '')URLURL的查询字符串,并且必须附加时间戳,以避免304响应缺少Access-Control-Allow-Origin标题。

这给我

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');

3
在本地文件上没有服务器的情况下尝试此操作将提供:从源'null'访问'file:/// C:/.../img/colorPaletteCtrl.png?1507058959277'中的图像已被CORS策略阻止:无效的响应。因此,不允许访问原始“空”。
桑福德·斯塔布

1
简单地imgObj.setAttribute('crossOrigin', '')为我解决了-谢谢!img.crossOrigin = "Anonymous"工程,以及(如提到这里)。@SanfordStaab浏览器将本地文件视为位于其他域中,否则网站所有者可以读取您的本地文件。您需要从同一域请求图像,或者响应请求的标头需要告诉浏览器其他域的代码可以读取该文件。

19

您将无法直接从另一台服务器将图像绘制到画布中,然后使用getImageData。这是一个安全问题,画布将被视为“已污染”。

使用PHP将映像副本保存到服务器,然后仅加载新映像是否对您有用?例如,您可以将URL发送到PHP脚本并将其保存到服务器,然后将新文件名返回给javascript,如下所示:

<?php //The name of this file in this example is imgdata.php

  $url=$_GET['url'];

  // prevent hackers from uploading PHP scripts and pwning your system
  if(!@is_array(getimagesize($url))){
    echo "path/to/placeholderImage.png";
    exit("wrong file type.");
  }

  $img = file_get_contents($url);

  $fn = substr(strrchr($url, "/"), 1);
  file_put_contents($fn,$img);
  echo $fn;

?>

您可以将PHP脚本与一些ajax javascript一起使用,如下所示:

xi=new XMLHttpRequest();
xi.open("GET","imgdata.php?url="+yourImageURL,true);
xi.send();

xi.onreadystatechange=function() {
  if(xi.readyState==4 && xi.status==200) {
    img=new Image;
    img.onload=function(){ 
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
    }
    img.src=xi.responseText;
  }
}

如果getImageData此后在画布上使用,它将正常工作。

另外,如果您不想保存整个图像,则可以将x和y坐标传递到PHP脚本并在该侧计算像素的rgba值。我认为有很好的库可以在PHP中进行这种图像处理。

如果您想使用这种方法,请告诉我是否需要帮助来实施。

edit-1:peeps指出php脚本是公开的,并允许Internet潜在地恶意使用它。有上百万种方法可以解决此问题,最简单的方法之一就是某种形式的URL混淆...我认为安全的php做法值得单独使用google; P

edit-2:根据普遍的需求,我添加了一个检查以确保它是一个图像而不是一个php脚本(来自:PHP检查文件是否是一个图像)。


15
另外-从本地文件系统加载图像和脚本会产生相同的问题。
Guy Dafny 2014年

2
那不是很安全,有人可能会使用php脚本恶意地用大文件恶意泛滥您的服务器,这将是不好的
LAX1DUDE

1
@ LAX1DUDE答案通过简单的安全检查得到了改进
Jumpjack

1
借助此方法,还可以将图像传递给tesseract.js OCR引擎,而无需使用node.js(只需在img.onload()函数中添加对Tesseract.recognize(img)的调用即可)。–
jumpjack

1
您需要验证用户提供的文件名,文件/ MIME类型,上传位置和大小。此代码容易受到多种攻击。我将其作为练习留给读者,但我相信使用此代码可以将.php文件上传到Web服务器的根目录。
hiburn8

4

我在Chrome本地测试代码时看到此错误。我切换到了Firefox,再也看不到错误了。也许切换到另一个浏览器是一个快速修复。

如果您使用的是第一个答案中给出的解决方案,请确保img.crossOrigin = "Anonymous";在声明img变量后立即添加(例如var img = new Image();)。


2

您的问题是您加载了外部映像,即从另一个域加载。当您尝试访问画布上下文的任何数据时,这会导致安全错误。


是的,我从上传子域获取图片...该怎么办?
Erfan Safarpoor

1
好吧,您可以使用一个代理来检索您的图像,该代理将与您在其上运行画布的域位于同一域中。
axelduch

1
我正在从“我的电脑”中上传新图像,并尝试将其加载到画布中,但出现此错误。我没有使用任何类型的服务器映像。
Piyush Dholariya 2015年

脚本所在的服务器(但未执行)将您的计算机视为外部域,因为复制到画布中的图像是在实际执行脚本的计算机上创建的。
Jumpjack '18

2

您正在通过跨源域加载来“污染”画布。查看此MDN文章:

https://developer.mozilla.org/zh-CN/docs/HTML/CORS_Enabled_Image


我添加了<IfModule mod_setenvif.c> <IfModule mod_headers.c> <FilesMatch“ \。(cur | gif | ico | jpe?g | png | svgz?| webp)$”> SetEnvIf Origin“:” IS_CORS标头集访问权限- Control-Allow-Origin“ *” env = IS_CORS </ FilesMatch> </ IfModule> </ IfModule>可以访问所有域,但是不起作用!
Erfan Safarpoor

1
您正在使用哪种浏览器进行测试?并确保您重新启动了Apache。
srquinn 2014年

我无法重新启动apache ...我有共享的cpanel帐户。
Erfan Safarpoor 2014年

2
抱歉,无法帮助您配置Apache设置。这也超出了这个问题的范围。将有关Apache设置的麻烦发布到一个新问题,社区将很乐意为您提供帮助。
srquinn 2014年

1

在本地工作时,添加服务器

在本地工作时,我遇到了类似的问题。您的url将是本地文件的路径,例如file:///Users/PeterP/Desktop/folder/index.html。

请注意,我在使用MAC。

我通过全局安装http-server来解决这个问题。https://www.npmjs.com/package/http-server

脚步:

  1. 全局安装: npm install http-server -g
  2. 运行服务器: http-server ~/Desktop/folder/

PS:我假设您已经安装了节点,否则您将无法获得运行非常快的npm命令。


0

正如matt burns在他的回答中所说,您可能需要在托管问题映像的服务器上启用CORS。

如果服务器是Apache,则可以通过将以下代码段(从此处开始)添加到VirtualHost配置或.htaccess文件中来完成:

<IfModule mod_setenvif.c>
    <IfModule mod_headers.c>
        <FilesMatch "\.(cur|gif|ico|jpe?g|png|svgz?|webp)$">
            SetEnvIf Origin ":" IS_CORS
            Header set Access-Control-Allow-Origin "*" env=IS_CORS
        </FilesMatch>
    </IfModule>
</IfModule>

...如果将其添加到VirtualHost,您可能还需要重新加载Apache的配置(例如,sudo service apache2 reload如果Apache在Linux服务器上运行)


0

我的问题太混乱了,我只对图像进行了base64编码,以确保不会出现任何CORS问题


-2

我今天遇到了同样的问题,并通过下面的代码解决了。

html代码:

<div style='display: none'>
  <img id='img' src='img/iak.png' width='600' height='400' />
</div>
<canvas id='iak'>broswer don't support canvas</canvas>

js代码:

var canvas = document.getElementById('iak')
var iakImg = document.getElementById('img')
var ctx = canvas.getContext('2d')
var image = new Image()
image.src=iakImg.src
image.onload = function () {
   ctx.drawImage(image,0,0)
   var data = ctx.getImageData(0,0,600,400)
}

像上面的代码,并且没有跨域问题。


1
src值仍然是跨源来源,因此如何避免该问题?
Loveen Dyall

当运行本地HTML文件(无服务器)(例如file:/// X:/htmlfile.html)时,此方法有效。大多数人以“;”结尾。“ var data = ..”语句的意义是什么?我想要解释为什么可以避免“跨域”错误。但是,确实如此,谢谢。
dcromley

双重聪明的凯里
Mari Selvan

1
@dcromley“大多数人以”;“结尾。不建议按照标准使用分号。分号只会增加工作量,看起来很难看,没有它们,代码也可以完美运行。检查github.com/standard/standard
madprops 18-4-16

1
@madprops developer.mozilla.org说:“ JavaScript应用程序由具有适当语法的语句组成。单个语句可能跨越多行。如果每个语句之间用分号分隔,则可能会在一行上出现多个语句。这不是关键字,但有一组关键字。” 因此,我处于“始终使用”派系。我什至不知道那里有一个“从不”派系。我认为后者也相信地球是平坦的?(开个玩笑-谢谢)
dcromley

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.