如何修复 getImageData() 错误画布已被跨域数据污染?
Posted
技术标签:
【中文标题】如何修复 getImageData() 错误画布已被跨域数据污染?【英文标题】:How to fix getImageData() error The canvas has been tainted by cross-origin data? 【发布时间】:2014-04-01 14:35:18 【问题描述】:我的代码在我的本地主机上运行良好,但在站点上却无法运行。
我从控制台收到此错误,对于这一行 .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;
注意:我的图片 url (src) 来自子域 url
【问题讨论】:
即使 img.src 是本地相对 url:“img/foo.png”,我也会收到此错误 - 那么这个跨域是什么? 参见***.com/a/16218015/5175433:“Chrome 不会认为不同的本地文件来自同一个域。” 【参考方案1】:您的问题是您加载了外部图像,这意味着来自另一个域。当您尝试访问画布上下文的任何数据时,这会导致安全错误。
【讨论】:
是的,我从上传子域获取图片...我该怎么办? 您可以使用代理来检索您的图像,该代理将与您运行画布的代理位于同一域中。 我正在从“我的电脑”上传新图像并尝试将其加载到画布中,但出现此错误。我没有使用任何类型的服务器映像。 脚本所在的服务器(但未执行)将您的计算机视为外部域,因为复制到画布中的图像是在您的计算机上创建的,脚本实际上是在该计算机上执行的。 【参考方案2】:您正在通过从跨域域加载来“污染”画布。查看这篇 MDN 文章:
https://developer.mozilla.org/en-US/docs/html/CORS_Enabled_Image
【讨论】:
我添加了您将无法将图像直接从另一台服务器绘制到画布中,然后使用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 脚本已暴露并允许互联网恶意使用它。有一百万种方法可以处理这个问题,其中最简单的一种是某种 URL 混淆......我认为安全的 php 实践值得一个单独的谷歌;P
edit-2:根据大众的需求,我添加了一个检查以确保它是图像而不是 php 脚本(来自:PHP check if file is an image)。
【讨论】:
另外 - 从本地文件系统加载图像和脚本,也会出现同样的问题。 这不是很安全,有人可能会使用 php 脚本恶意用大文件淹没您的服务器,这会很糟糕 @LAX1DUDE 答案通过简单的安全检查得到改进 感谢此方法,也可以在不使用 node.js 的情况下将图像传递给 tesseract.js OCR 引擎(只需在 img.onload() 函数中添加对 Tesseract.recognize(img) 的调用. 您需要验证用户提供的上传文件名、文件/mime 类型、位置和大小。此代码易受多种攻击。我将把它作为练习留给读者,但我相信可以使用此代码将 .php 文件上传到网络服务器的根目录。【参考方案4】:正如其他人所说,您通过从跨域域加载来“污染”画布。
https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image
但是,您可以通过简单的设置来防止这种情况发生:
img.crossOrigin = "Anonymous";
这仅在远程服务器正确设置以下标头时才有效:
Access-Control-Allow-Origin "*"
使用“直接链接”选项时的Dropbox file chooser 就是一个很好的例子。我在oddprints.com 上使用它来将远程Dropbox 图像URL 中的图像存储到我的画布中,然后将图像数据提交回我的服务器。全部在javascript中
【讨论】:
这可以在将 imgur 图像加载到 jsfiddle.net 时修复此错误 如果我先放置 crossorigin 然后设置源然后访问数据 onload 对我有用 如果它是一个本地文件,而您的应用根本不使用互联网怎么办?就像 android webview 应用程序一样,图像与 html 文件位于同一文件夹中。这并不能解决问题。【参考方案5】:我发现我必须使用 .setAttribute('crossOrigin', '')
并且必须在 URL 的查询字符串中附加一个时间戳以避免缺少 Access-Control-Allow-Origin
标头的 304 响应。
这给了我
var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
【讨论】:
在本地文件上没有服务器的情况下尝试此操作会给出:从原点“null”访问“file:///C:/.../img/colorPaletteCtrl.png?1507058959277”处的图像有被 CORS 策略阻止:无效响应。因此,不允许访问 Origin 'null'。 简单地使用imgObj.setAttribute('crossOrigin', '')
为我解决了这个问题 - 谢谢! img.crossOrigin = "Anonymous"
也可以(如提到的here)。 @SanfordStaab 浏览器将本地文件视为位于不同域中,否则网站所有者可以读取您的本地文件。您需要从同一个域请求图像,或者对请求的响应中的标头需要告诉浏览器,其他域的代码可以读取该文件。【参考方案6】:
正如 matt burns 在 his answer 中所说,您可能需要在托管问题图像的服务器上启用 CORS。
如果服务器是 Apache,这可以通过将以下 sn-p(来自 here)添加到您的 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 服务器上运行)
【讨论】:
【参考方案7】:我今天遇到同样的问题,通过下面的代码解决。
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)
代码如上,不存在跨域问题。
【讨论】:
src 值仍然是跨源源,如何避免这个问题? 这种方法适用于运行本地 html 文件(无服务器),如 file:///X:/htmlfile.html。大多数人用“;”结束他们的陈述。 “var data = ..”语句的意义何在?我想解释一下为什么这可以避免“跨域”错误。但确实如此,谢谢。 双智能卡里 @dcromley “大多数人的陈述都以 ; 结尾。”根据标准,不建议使用分号。分号只是增加了更多的工作,看起来很丑,没有它们,代码也能完美运行。检查github.com/standard/standard @madprops developer.mozilla.org 说“JavaScript 应用程序由具有适当语法的语句组成。单个语句可能跨越多行。如果每个语句由分号。这不是一个关键字,而是一组关键字。所以我属于“总是使用”派。我什至不知道有一个“从不”派系。我假设后者也相信地球是平的? (开个玩笑——谢谢)【参考方案8】:我遇到了同样的问题,对我来说,只需连接 https:$image.getAttribute('src')
【讨论】:
请解释一下你做了什么【参考方案9】:在本地工作时添加服务器
我在本地工作时遇到了类似的问题。您的 url 将成为本地文件的路径,例如file:///Users/PeterP/Desktop/folder/index.html.
请注意,我使用的是 MAC。
我通过全局安装 http-server 解决了这个问题。 https://www.npmjs.com/package/http-server
步骤:
-
全局安装:
npm install http-server -g
运行服务器:http-server ~/Desktop/folder/
PS:我假设你已经安装了节点,否则你不会跑很远的 npm 命令。
【讨论】:
我很感激。很好的工作。糟糕的是,如果您只想将编码作为一种爱好并让您的生活更轻松更好,那么必须有一种解决方法。这么多看起来不必要的箍。这只是一个例子。叹息……【参考方案10】:当我在本地测试我的代码时,我在Chrome
上看到了这个错误。我切换到Firefox
,我不再看到错误。也许切换到另一个浏览器是一种快速解决方法。
如果您使用第一个答案中给出的解决方案,请确保在声明 img
变量后添加 img.crossOrigin = "Anonymous";
(例如 var img = new Image();
)。
【讨论】:
【参考方案11】:我的问题太混乱了,我只是对图像进行了 base64 编码,以确保不会有任何 CORS 问题
【讨论】:
【参考方案12】:您可以将图像转换为数据字符串,以使用图像源而不是实际图像源。
[https://www.base64-image.de/][1] 将图像转换为数据字符串。
从上述网站转换和复制字符串数据。
设置 image.src =
【讨论】:
【参考方案13】:解决方法, 将源图片 URL 转换为 Base64 数据并分配给 img
例如,使用 Axios
const getBase64 = async(url)=>
try
let image = await axios.get(url, responseType: 'arraybuffer' );
let raw = Buffer.from(image.data).toString('base64');
return "data:" + image.headers["content-type"] + ";base64,"+raw;
catch (error)
console.log(error)
var image = new Image()
image.src=getBase64(url)
canvas 没有跨域依赖
【讨论】:
以上是关于如何修复 getImageData() 错误画布已被跨域数据污染?的主要内容,如果未能解决你的问题,请参考以下文章
已解决:从线性数组中获取相邻像素(如 context.getImageData 中)