Jump to content

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.


ForgeableSum
 Share

Recommended Posts

Uncaught SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported.

 

This happens when I try to use toDataURL on the canvas of a bitmap data object which has had images draw to it not from the local server. Based on everything I've read online, setting the crossOrigin attribute to anonymous on the images being drawn is a fix for this. But it doesn't work if I set this on all the images in the cache. I do this in create because there's no guarantee all the images will have finished loading in the load state:

 

$.each(game.cache._images, function(key, image) {            image.data.setAttribute('crossOrigin', 'anonymous');        });
Link to comment
Share on other sites

Based on everything I've read online, setting the crossOrigin attribute to anonymous on the images being drawn is a fix for this. 

 

 

I think you should investigate status of CORS requests and responses in the Network tab of your browser. 

 

Let's look on what happens here...

 

"By default" in most general case what you are trying to do is impossible and prohibited by browser (no matter what fancy attributes you add). Imagine that you could do something like this without server permission. In such case, if I could somehow learn that your top-secret future game is hosted at http://127.0.0.1/topsecret (local private URL), I can send you a link to my own site. And when you visit it, javascript on such page will pull things from your local server (which you have access to), and post them to my site. Neat, right? :)

 

Moreover, in such an unlimited world I could still you personal photos in the background, querying them from your facebook page (as your browser carries all needed authorization cookies), and posting to my website.

 

Therefore, browser needs to ask in the request "can I please use this images any way I want". And, what is important, server has to respond: "Sure, this is public info, go ahead".

 

So two things to double check:

- Set crossOrigin attribute BEFORE setting src attribute. If request is made, it might be too late to ask permission. :) https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

- Check that server indeed responded with CORS approval. If not, you are out of luck, and simply can't do anything. You cannot prevent tainting, if "foreign" server does not allow you to do this.

Link to comment
Share on other sites

I think you should investigate status of CORS requests and responses in the Network tab of your browser. 

 

Let's look on what happens here...

 

"By default" in most general case what you are trying to do is impossible and prohibited by browser (no matter what fancy attributes you add). Imagine that you could do something like this without server permission. In such case, if I could somehow learn that your top-secret future game is hosted at http://127.0.0.1/topsecret (local private URL), I can send you a link to my own site. And when you visit it, javascript on such page will pull things from your local server (which you have access to), and post them to my site. Neat, right? :)

 

Moreover, in such an unlimited world I could still you personal photos in the background, querying them from your facebook page (as your browser carries all needed authorization cookies), and posting to my website.

 

Therefore, browser needs to ask in the request "can I please use this images any way I want". And, what is important, server has to respond: "Sure, this is public info, go ahead".

 

So two things to double check:

- Set crossOrigin attribute BEFORE setting src attribute. If request is made, it might be too late to ask permission. :) https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_enabled_image

- Check that server indeed responded with CORS approval. If not, you are out of luck, and simply can't do anything. You cannot prevent tainting, if "foreign" server does not allow you to do this.

Thanks for the info!

 

I actually have complete control over the server serving the images. It's an s3 bucket with AWS and there is a way to configure CORS with an XML configuration file:

http://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html

 

I'm not quite sure though, what my CORS configuration file should look like. I've tried these two configurations (with no luck):

 

<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>
 
   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>
   <AllowedMethod>GET</AllowedMethod>
   <AllowedHeader>*</AllowedHeader>
 </CORSRule>
 
 
 
</CORSConfiguration>
 
 
 
 
<CORSConfiguration>
    <CORSRule>
        <AllowedOrigin>*</AllowedOrigin>
        <AllowedMethod>GET</AllowedMethod>
        <MaxAgeSeconds>3000</MaxAgeSeconds>
        <AllowedHeader>Authorization</AllowedHeader>
    </CORSRule>
</CORSConfiguration>

 

 

Is there a specific header I need to allow? To be honest, this whole CORS thing confuses me. If I already have access to the file, shouldn't I be able to manipulate it/change it in whatever way I want, regardless of permissions? I already have access to the file ... and toDataURL just converts it into a URL string. How does the specific act of converting it to a URL string create some kind of security loophole that can be exploited?
 
CORS sets restrictions on how you can use an image .. but if you've already downloaded the image to the client - when you edit that image, you're not editing anything on the server. You're just editing the copy of the image downloaded from the server. Why should there be security restrictions at that point? I'm struggling to understand. 
Link to comment
Share on other sites

Maybe if you have full control it is easier to just put everything under single domain with proper DNS setup? (if your site is aaa.example.com, point your image buckets as image.aaa.example.com)

 

Have you checked actual HTTP request and response? Do you see proper CORS exchange? One thing as I noted might be that you are setting CORS attribute on image tag too late, and simply not firing a request. 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

 

I'm struggling to understand. 

 

The key here is that there are three parties in a potential malicious scenario:

A: Secret server to what you now have access (in your local network, for example, or by acquired authentication token/cookie, or by your IP address). 

B: Public server with potentially malicious script.

C: Your browser

 

Your browser C has access both to A and B. 

 

Web page served from server B is free to load images from anywhere and display them in browser window C. There is no harm in showing you the images, that you have legit access to. 

 

But if browser C ever allow page from server B to access pixel data of the loaded images, this page can parse and submit your secret image to server B.

 

Therefore browser C requires you to ask server A: "Is it ok that someone sees the data that you are about to give without requesting user consent?!". And server can respond:

- No, this is private nude photos :)

- Sure, this is public anyway, anyone can see.

- Yes, but this is private image, only trusted.example.com can manipulate images

Link to comment
Share on other sites

Okay folks, I've figured it out and the solution is surprisingly simple. Despite the fact that I had my S3 CORS config file set correctly, I was still getting the tainted canvas error. Your answer really helped me out, sbat, because I realized i needed to set the cross origin attribute before the src is set on image objects, which means it would have to be inside phaser core files. While going through line by line, into the nitty-gritty of how Phaser.Loader creates images, I came across the loadImageTag function which had a check at the very top for the crossOrigin property.. what the hell is this? I took a look at the docs for Phaser.Loader and low and behold, there it was, in simple, plain English:

 

The crossOrigin value applied to loaded images. Very often this needs to be set to 'anonymous'.
 

 

 

Setting that property solved all my problems. 

 

Moral of the story: consult the docs first. 

Link to comment
Share on other sites

 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...