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

IT技术 javascript html canvas cross-domain
2021-02-01 17:31:51

是我睡眠不足还是怎么的?下面这段代码

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

这绝不可能行不通!任何人都可以解释一下吗?

6个回答

规格中它说:

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

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

@AlfonsoML,也许我错了,但是“如果攻击者能够猜出您在私人站点中拥有的图片的名称”,他可能会使用浏览器使用 curl non 抓取图像。我的观点:公共 URL 公共内容。
2021-03-17 17:31:51
@pop850 即使我使用数据 URL,我也面临这个问题。有没有办法可以解决数据 URL 的问题?
2021-03-23 17:31:51
如果攻击者能够猜出您在私人站点中拥有的图片的名称,他将能够通过在画布上进行绘画并将新图像发送到他的站点来获取该图片的副本。从我的角度来看,主要限制是避免绘制另一个站点的内容,但安全性太复杂了,因为攻击者可以在任何意外站点中找到漏洞。
2021-03-24 17:31:51
@kilianc 存在此限制是为了防止攻击者使您的浏览器(带有身份验证 cookie)获取图像并将其内容发送给攻击者;攻击者没有您的 cookie,因此他无法使用 curl 获取您有权获取的相同资源。“公共 URL,公共内容”是一种相当有缺陷的思维方式:我的 Facebook 页面具有面向公众的组件,但只有使用正确的身份验证令牌才能访问很多内容。
2021-03-29 17:31:51
请注意,子域也很重要。根据我的经验,至少在 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 中的视频或图像时
2021-04-04 17:31:51

在对我有用的图像对象上设置交叉原点属性(我使用的是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;
};
我确实使用了fabricjs但无法解决它。您将如何使用此代码执行此操作?function 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; } }
2021-03-14 17:31:51
谢谢,它在 Chrome 中对我有用。我没有使用fabric.js
2021-03-22 17:31:51
也适用于 Firefox。
2021-03-25 17:31:51

如果图像托管在设置了 Access-Control-Allow-Origin 或 Access-Control-Allow-Credentials 的主机上,您可以使用跨源资源共享 (CORS)。有关更多详细信息,请参见此处(crossorigin 属性)。

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

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

嗨,我就是这样做的,我在图像上有 Access-Control-Allow-Origin: *,我有 crossorigin='anonymous',然后我在画布上绘制图像,然后我调用 toDataUrl 并且我仍然得到 SECURITY_ERR : DOM 异常 18
2021-03-23 17:31:51

如果图像托管能够为图像资源提供以下 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的通配符值
2021-03-29 17:31:51

最后我找到了解决方案。只需要crossOriginfromURLfunc 中添加第三个参数

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