Why does canvas.toDataURL() throw a security exception?
Asked Answered
E

10

95

Did I not get enough sleep or what? This following code

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"));
}

is throwing this error:

SECURITY_ERR: DOM Exception 18

There's no way this shouldn't work! Can anyone explain this, please?

Extravaganza answered 5/3, 2010 at 22:0 Comment(2)
See here for a solution: How to use canvas.toDataURL() to get base64 of image in Adobe AIR?Calumet
That solution doesn't seem useful to people not using Adobe AIR.Dilworth
T
69

In the specs it says:

Whenever the toDataURL() method of a canvas element whose origin-clean flag is set to false is called, the method must raise a SECURITY_ERR exception.

If the image is coming from another server I don't think you can use toDataURL()

Townsman answered 5/3, 2010 at 22:24 Comment(8)
thanks so much! like Mike R. I can't imagine how this could be security-related! :)Extravaganza
If an attacker is able to guess the name of a picture that you have in a private site, he would be able to get a copy of it by painting in on a canvas and sending the new image to his site. The main restriction from my point of view is to avoid drawing the contents of another site, but security is too complex as the attacker can find a hole in any unexpected site.Diella
Note that the subdomain matters as well. In my experience, in Chrome at least, a SECURITY_ERR: DOM Exception 18 is raised when making a call that is perceived to be across subdomains: 1. in example.com/some/path/index.html for a video or image in foo.example.com 2. when going to the same page as in 1 but by entering the URL example.com/some/path/index.html and then attempting to call toDataUrl() for a video or image in www.example.comDewdrop
@AlfonsoML, maybe I'm wrong, but "If an attacker is able to guess the name of a picture that you have in a private site" probably he will grab the image with a curl non with a browser. My point of view: Public URL Public Content.Arielle
@Extravaganza I'm facing this issue even when I use a data URL. Is there a way that I can workaround this for data URLs?Howells
@Townsman let say i have a canvas to hold my clipboard image. Are you saying that i cant use toDataURL()? Please advice.Ody
@Arielle This restriction exists to prevent an attacker from causing your browser (with authentication cookies) to fetch an image and send its contents to an attacker; the attacker doesn't have your cookies, so he can't use curl to get the same resources you are authorized to get. "Public URL, Public Content" is a rather flawed way of thinking: my Facebook page has public facing components, but there is much that is only accessible with the right authentication token.Cone
This is the reason bur where is the soloution?Curative
W
21

Setting cross origin attribute on the image objects worked for me (i was using 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';

For those using fabricjs, here's how to patch 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;
};
Whiskey answered 17/4, 2013 at 19:10 Comment(4)
Thanks, it works for me in Chrome. I am not using fabric.js thoughElodea
I do use fabricjs but could not solve it. How would you do this with this code? 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; } }Boast
Works in Firefox as well.Rahel
I slightly adjusted your code, but it really helped me.Metronome
U
17

If the image is hosted on a host that sets either of Access-Control-Allow-Origin or Access-Control-Allow-Credentials, you can use Cross Origin Resource Sharing (CORS). See here (the crossorigin attribute) for more details.

Your other option is for your server to have an endpoint that fetches and serves an image. (eg. http://your_host/endpoint?url=URL) The downside of that approach being latency and theoretically unnecessary fetching.

If there are more alternate solutions, I'd be interested in hearing about them.

Ursula answered 9/11, 2011 at 12:31 Comment(1)
Hi, I did just that, I have Access-Control-Allow-Origin: * on the images, I have crossorigin='anonymous', then I paint the image on canvas, then I call toDataUrl and I'm still getting the SECURITY_ERR: DOM Exception 18Insulin
T
14

Seems there is a way to prevent that if image hosting able to provide the following HTTP headers for the image resources and browser supports CORS:

access-control-allow-origin: *
access-control-allow-credentials: true

It is stated here: http://www.w3.org/TR/cors/#use-cases

Trivial answered 7/4, 2011 at 23:7 Comment(1)
access-control-allow-credentials: true will not work with access-control-allow-origin: *. When the former is set, the latter should have an origin value, not a wildcard value from developer.mozilla.org/en-US/docs/HTTP/Access_control_CORSTeresa
A
4

Finally i found the solution. Just need add the crossOrigin as third param in fromURL func

fabric.Image.fromURL(imageUrl, function (image) {
            //your logic
    }, { crossOrigin: "Anonymous" });
Ancon answered 6/1, 2017 at 16:36 Comment(0)
M
3

I had the same problem and all the images are hosted in the same domain... So, if someone is having the same problem, here is how I solved:

I had two buttons: one to generate the canvas and another one to generate the image from the canvas. It only worked for me, and sorry that I don't know why, when I wrote all the code on the first button. So when I click it generate the canvas and the image at the same time...

I always have this security problem when the codes were on different functions... =/

Marcel answered 18/9, 2013 at 1:39 Comment(0)
A
2

I was able to make it work using this:

Write this on first line of your .htaccess on your source server

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

Then when creating an <img> element, do it as follows:

// 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');
Artichoke answered 29/6, 2014 at 11:21 Comment(0)
P
1

You can't put spaces in your ID

Update

My guess is that image is on a different server than where you're executing the script. I was able to duplicate your error when running it on my own page, but it worked fine the moment I used an image hosted on the same domain. So it's security related - put the image on your site. Anyone know why this is the case?

Patroon answered 5/3, 2010 at 22:1 Comment(0)
P
1

If you are simply drawing some images on a canvas, make sure you are loading the images from the same domain.

www.example.com is different to example.com

So make sure your images and the url you have in your address bar are the same, www or not.

Pismire answered 24/10, 2013 at 8:36 Comment(0)
W
-1

I'm using fabric.js and could resolve this by using toDatalessJSON instead of toDataURL:

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

Edit: Nevermind. This results in just the background image being exported to JPG, without the drawing on top so it was not entirely useful after all.

Welladvised answered 17/1, 2018 at 14:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.