为啥 canvas.toDataURL() 会抛出安全异常?

Posted

技术标签:

【中文标题】为啥 canvas.toDataURL() 会抛出安全异常?【英文标题】:Why does canvas.toDataURL() throw a security exception?为什么 canvas.toDataURL() 会抛出安全异常? 【发布时间】:2011-01-24 07:36:31 【问题描述】:

是我睡眠不足还是怎么回事?以下代码

var frame=document.getElementById("viewer");
frame.width=100;
frame.height=100;

var ctx=frame.getContext("2d");
var img=new Image();
img.src="http://www.ansearch.com/images/interface/item/small/image.png"

img.onload=function() 
    // draw image
    ctx.drawImage(img, 0, 0)

    // Here's where the error happens:
    window.open(frame.toDataURL("image/png"));

正在抛出此错误:

SECURITY_ERR: DOM Exception 18

这不可能不工作!谁能解释一下?

【问题讨论】:

解决方案见此处:How to use canvas.toDataURL() to get base64 of image in Adobe AIR? 对于不使用 Adob​​e AIR 的人来说,该解决方案似乎没有用。 【参考方案1】:

在specs 中写道:

每当一个 toDataURL() 方法 其 origin-clean 标志的 canvas 元素 设置为 false 被调用,该方法 必须引发 SECURITY_ERR 异常。

如果图像来自另一台服务器,我认为您不能使用 toDataURL()

【讨论】:

如果攻击者能够猜出您在私人站点中的图片名称,他将能够通过在画布上绘制并将新图像发送到他的网站。从我的角度来看,主要的限制是避免绘制另一个站点的内容,但是安全性太复杂了,因为攻击者可以在任何意外站点中找到漏洞。 请注意,子域也很重要。根据我的经验,至少在 Chrome 中,当进行被认为是跨子域的调用时,会引发 SECURITY_ERR: DOM Exception 18: 1. 在example.com/some/path/index.html 中获取 foo.example.com 中的视频或图像 2. 当去到与 1 相同的页面,但输入 URL example.com/some/path/index.html,然后尝试调用 toDataUrl() 以获取 www.example.com 中的视频或图像 @AlfonsoML,也许我错了,但是“如果攻击者能够猜出您在私人站点中的图片名称”,他可能会抓取带有 curl 非浏览器。我的观点:公共 URL 公共内容。 @pop850 即使我使用数据 URL,我也面临这个问题。有没有办法可以解决数据 URL 的问题? @kilianc 存在此限制是为了防止攻击者导致您的浏览器(带有身份验证cookie)获取图像并将其内容发送给攻击者;攻击者没有你的 cookie,所以他不能使用 curl 来获取你被授权获取的相同资源。 “公共 URL,公共内容”是一种相当有缺陷的思维方式:我的 Facebook 页面有面向公众的组件,但有很多只有通过正确的身份验证令牌才能访问。【参考方案2】:

在图像对象上设置交叉原点属性对我有用(我使用的是 fabricjs)

    var c = document.createElement("img");
    c.onload=function()
        // add the image to canvas or whatnot
        c=c.onload=null
    ;
    c.setAttribute('crossOrigin','anonymous');
    c.src='http://google.com/cat.png';

对于那些使用fabricjs的人,这里是如何修补Image.fromUrl

// patch fabric for cross domain image jazz
fabric.Image.fromURL=function(d,f,e)
    var c=fabric.document.createElement("img");
    c.onload=function()
        if(f)f(new fabric.Image(c,e))
        c=c.onload=null
    ;
    c.setAttribute('crossOrigin','anonymous');
    c.src=d;
;

【讨论】:

谢谢,它适用于我的 Chrome。虽然我没有使用 fabric.js 我确实使用了fabricjs,但无法解决。您将如何使用此代码执行此操作?函数 wtd_load_bg_image( img_url ) if( img_url ) var bg_img = new Image(); bg_img.onload = function() canvasObj.setBackgroundImage(bg_img.src, canvasObj.renderAll.bind(canvasObj), originX: 'left', originY: 'top', left: 0, top: 0 ); ; bg_img.src = img_url; 也适用于 Firefox。【参考方案3】:

如果图像托管在设置了 Access-Control-Allow-Origin 或 Access-Control-Allow-Credentials 的主机上,您可以使用跨域资源共享 (CORS)。 See here (the crossorigin attribute) for more details.

您的另一个选择是让您的服务器有一个端点来获取和提供图像。 (例如http://your_host/endpoint?url=URL) 这种方法的缺点是延迟和理论上不必要的获取。

如果有更多替代解决方案,我有兴趣了解它们。

【讨论】:

嗨,我就是这样做的,我在图像上有 Access-Control-Allow-Origin: *,我有 crossorigin='anonymous',然后我在画布上绘制图像,然后调用 toDataUrl我仍然收到 SECURITY_ERR: DOM Exception 18【参考方案4】:

如果图像托管能够为图像资源提供以下 HTTP 标头并且浏览器支持 CORS,似乎有一种方法可以防止这种情况:

访问控制允许来源:*
访问控制允许凭据:true

这里声明:http://www.w3.org/TR/cors/#use-cases

【讨论】:

access-control-allow-credentials: true 不适用于 access-control-allow-origin: *。当设置了前者时,后者应该有一个原始值,而不是来自developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS的通配符值【参考方案5】:

我终于找到了解决方案。只需将crossOrigin 添加为fromURL func 中的第三个参数

fabric.Image.fromURL(imageUrl, function (image) 
            //your logic
    ,  crossOrigin: "Anonymous" );

【讨论】:

【参考方案6】:

我遇到了同样的问题,所有图片都托管在同一个域中......所以,如果有人遇到同样的问题,我是这样解决的:

我有两个按钮:一个用于生成画布,另一个用于从画布生成图像。它只对我有用,很抱歉,当我在第一个按钮上编写所有代码时,我不知道为什么。因此,当我单击它时,会同时生成画布和图像...

当代码在不同的功能上时,我总是遇到这个安全问题... =/

【讨论】:

【参考方案7】:

我能够使用它来使它工作:

在源服务器上.htaccess 的第一行写下此内容

Header add Access-Control-Allow-Origin "*"

那么在创建<img>元素时,按如下方式进行:

// jQuery
var img = $('<img src="http://your_server/img.png" crossOrigin="anonymous">')[0]

// or pure
var img = document.createElement('img');
img.src='http://your_server/img.png';
img.setAttribute('crossOrigin','anonymous');

【讨论】:

【参考方案8】:

您的 ID 中不能有空格

更新

我的猜测是,该图像与您执行脚本的服务器位于不同的服务器上。在我自己的页面上运行它时,我能够复制您的错误,但是当我使用托管在同一域上的图像时,它运行良好。所以它与安全有关 - 将图像放在您的网站上。有人知道为什么会这样吗?

【讨论】:

【参考方案9】:

如果您只是在画布上绘制一些图像,请确保从同一域加载图像。

www.example.com 不同于 example.com

因此,请确保您的图片和地址栏中的 url 相同,无论是否为 www。

【讨论】:

【参考方案10】:

我使用的是 fabric.js,可以通过使用 toDatalessJSON 而不是 toDataURL 来解决这个问题:

canvas.toDatalessJSON( format: 'jpeg' ).objects[0].src

编辑:没关系。这导致仅将背景图像导出为 JPG,而顶部没有绘图,因此它毕竟不是完全有用的。

【讨论】:

以上是关于为啥 canvas.toDataURL() 会抛出安全异常?的主要内容,如果未能解决你的问题,请参考以下文章

iOS 上的 Safari 在调用 canvas.toDataURL 时抛出 SECURITY_ERR: DOM Exception 18

为啥我在html5中取到了canvas画布(已测试能取到),但是执行canvas.toDataURL("image.png")不成功,

为啥有时会抛出 FileNotFoundException

为啥 FileOutputStream 会抛出 FileNotFoundException?

为啥“prepareCall”会抛出 NumberFormatException?

为啥 BluetoothSetLocalServiceInfo 会抛出错误 1314?