How to fix getImageData() error The canvas has been tainted by cross-origin data?
Asked Answered
B

14

190

My code is working very well on my localhost but it is not working on the site.

I got this error from the console, for this line .getImageData(x,y,1,1).data:

Uncaught SecurityError: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data. 

part of my code:

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

Note: my image url (src) is from a subdomain url

Babin answered 28/2, 2014 at 14:22 Comment(2)
I'm getting this error even when the img.src is a local relative url: "img/foo.png" - so what is cross-origin about this?Refusal
See https://mcmap.net/q/136932/-i-get-a-quot-canvas-has-been-tainted-quot-error-in-chrome-but-not-in-ff: "Chrome does not consider different local files to be sourced from the same domain."Romance
H
175

As others have said you are "tainting" the canvas by loading from a cross origins domain.

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

However, you may be able to prevent this by simply setting:

img.crossOrigin = "Anonymous";

This only works if the remote server sets the following header appropriately:

Access-Control-Allow-Origin "*"

The Dropbox file chooser when using the "direct link" option is a great example of this. I use it on oddprints.com to hoover up images from the remote dropbox image url, into my canvas, and then submit the image data back into my server. All in javascript

Halogenate answered 8/1, 2015 at 12:23 Comment(5)
This worked to fix this error when loading an imgur image into jsfiddle.netHardiman
worked for me if I put crossorigin first then set source and then access data onloadBarkley
What if it's a local file, and your app doesn't use the internet at all? Like an android webview app and the image is just in the same folder as the html file. This doesn't solve it.Dogeatdog
This worked to fix this error when loading an imgur image into codepen.io See codepen.io/r-w-c/pen/RwELbwYMicrobicide
@Curtis: If the image is in the same folder, you (normally) don't have this problem. And if you have the problem, you can solve it this wayMicrobicide
R
61

I found that I had to use .setAttribute('crossOrigin', '') and had to append a timestamp to the URL's query string to avoid a 304 response lacking the Access-Control-Allow-Origin header.

This gives me

var url = 'http://lorempixel.com/g/400/200/';
var imgObj = new Image();
imgObj.src = url + '?' + new Date().getTime();
imgObj.setAttribute('crossOrigin', '');
Richardson answered 14/10, 2015 at 21:21 Comment(2)
Trying this without a server on a local file gives: Access to Image at 'file:///C:/.../img/colorPaletteCtrl.png?1507058959277' from origin 'null' has been blocked by CORS policy: Invalid response. Origin 'null' is therefore not allowed access.Refusal
Simply using imgObj.setAttribute('crossOrigin', '') solved it for me - thanks! img.crossOrigin = "Anonymous" works as well (as mentioned here). @SanfordStaab Browsers treat local files as being on a different domain, otherwise website owners could read your local files. You need to request images from the same domain, or the headers in the response to your request need to tell the browser that it's okay for code from other domains to read that file.Irritated
R
27

You won't be able to draw images directly from another server into a canvas and then use getImageData. It's a security issue and the canvas will be considered "tainted".

Would it work for you to save a copy of the image to your server using PHP and then just load the new image? For example, you could send the URL to the PHP script and save it to your server, then return the new filename to your javascript like this:

<?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;

?>

You'd use the PHP script with some ajax javascript like this:

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

If you use getImageData on the canvas after that, it will work fine.

Alternatively, if you don't want to save the whole image, you could pass x & y coordinates to your PHP script and calculate the pixel's rgba value on that side. I think there are good libraries for doing that kind of image processing in PHP.

If you want to use this approach, let me know if you need help implementing it.

edit-1: peeps pointed out that the php script is exposed and allows the internet to potentially use it maliciously. there are a million ways to handle this, one of the simplest being some sort of URL obfuscation... i reckon secure php practices deserves a separate google ;P

edit-2: by popular demand, I've added a check to ensure it is an image and not a php script (from: PHP check if file is an image).

Resendez answered 23/4, 2014 at 5:36 Comment(6)
As addition - Loading the image and script from local file system, makes same problem.Sammy
that's not very secure somebody could maliciously flood your server with large files using the php script which would be badTrombley
@Trombley answer improved with a simple security checkSunny
thanks to this method it is also possible to pass an image to tesseract.js OCR engine without using node.js (just add the call to Tesseract.recognize(img) in img.onload() function.Sunny
You NEED to validate the file name, file/mime type, location & size of uploads provided by users. This code is vulnerable to several attacks. I'll leave it as an exercise to the reader, but i believe its possible to upload .php files to the root of the webserver with this code.Straub
follow the link to "PHP check if file is an image" if you want to impose limits on image dimensions and type. you can also add a maximum length (bytecount) for $img.Resendez
B
6

When working on local, add a server.

I had a similar issue when working on local. Your URL is going to be the path to the local file, for example, file:///Users/PeterP/Desktop/folder/index.html.

Please note that I am on a Mac.

I got around this by installing an HTTP server globally. I used https://www.npmjs.com/package/http-server

Steps

  1. Global install: npm install http-server -g
  2. Run server: http-server ~/Desktop/folder/

These steps assume that you have node installed, otherwise you won't get very far running npm commands.

Braze answered 5/9, 2018 at 17:0 Comment(1)
I appreciate this. Nice work around. To bad there has to be a workaround if all you want to just enjoy coding as a hobby and to make your life easier and better. So many hoops that seem unnecessary. This is just one example. sigh...Elfreda
C
6

I was seeing this error on Chrome while I was testing my code locally. I switched to Firefox and I am not seeing the error any more. Maybe switching to another browser is a quick fix.

If you are using the solution given in the first answer, then make sure you add img.crossOrigin = "Anonymous"; just after you declare the img variable (for eg. var img = new Image();).

Clock answered 13/3, 2019 at 7:15 Comment(0)
H
6

My problem was so messed up I just base64 encoded the image to ensure there couldn't be any CORS issues

Hecklau answered 29/7, 2020 at 1:31 Comment(0)
S
3

Set the Image's crossOrigin attribute to Anonymous.

  let image = new Image();
  // image.src = ...;
  image.crossOrigin = `Anonymous`;
Suspiration answered 20/6, 2022 at 4:59 Comment(0)
M
2

Your problem is that you load an external image, meaning from another domain. This causes a security error when you try to access any data of your canvas context.

Mariko answered 28/2, 2014 at 14:30 Comment(4)
yes , i get image from upload subdomain... What do I do?Babin
Well you could use a proxy to retrieve your image, this proxy would be on the same domain as the one you are running your canvas on.Mariko
I am uploading new image from My Computer & try to load it in canvas & I am getting this error. I am not using any type of server image.Trousseau
your computer is seen by the server where the script is located (but not executed) as an external domain, as the image copied into the canvas is created on your computer, where the script is actually executed.Sunny
W
2

You are "tainting" the canvas by loading from a cross origins domain. Check out this MDN article:

https://developer.mozilla.org/en-US/docs/HTML/CORS_Enabled_Image

Wapiti answered 28/2, 2014 at 14:39 Comment(6)
i added <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> to htaccess all domains but not work!Babin
What browser are you testing in? And make sure you restarted Apache.Wapiti
i can't restart apache ... i have share cpanel account.Babin
Can't help you configure your Apache setup, sorry. Its also out of the scope of this question. Post your troubles regarding the Apache setup to a new question and the community will be happy to help you there.Wapiti
@srquinn: It is not out of the scope of this question. Many people dont have access to the web server (Apache), because they are not the owner (using a hosting provider) and so they can't set the headers, they can't change the servers CORS policy and they certainly cannot restart the server (Quote: "And make sure you restarted Apache").Microbicide
@Microbicide Adding CORs configuration to an Apache setup is not the question the OP asked. There is no mention of their server setup, Apache is not tagged, and this problem would occur with ANY web hosting solution that doesn't have CORs configured properly. Therefore, its best to ask Apache related questions in an Apache dedicated channel or for the OP to rephrase their question.Wapiti
R
0

As matt burns says in his answer, you may need to enable CORS on the server where the problem images are hosted.

If the server is Apache, this can be done by adding the following snippet (from here) to either your VirtualHost config or an .htaccess file:

<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>

...if adding it to a VirtualHost, you'll probably need to reload Apache's config too (eg. sudo service apache2 reload if Apache's running on a Linux server)

Ratepayer answered 15/6, 2016 at 15:26 Comment(0)
D
0

you can convert the image to a data string as use the image source instead of the actual image source.

[https://www.base64-image.de/][1] to convert image to data string. Convert and copy string data from the above website. set image.src = <copied_data_string>.

Debbee answered 23/3, 2021 at 18:29 Comment(0)
O
0

workaround solution, convert source image URL to Base64 data and assign to img

for example, use 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)

no cross-origin dependency from canvas

Organization answered 16/4, 2021 at 7:8 Comment(0)
H
-3

I meet the same problem today, and solve it by the code follows.

html code:

<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 code:

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

code like above, and there is no cross-domain problem.

Hills answered 15/6, 2017 at 9:22 Comment(5)
The src value is still a cross origin source so how does this avoid the issue?Perforation
This approach works when running a local html file (no server) like file:///X:/htmlfile.html. Most people end their statements with ";". What's the point of the "var data = .." statement? I'd like an EXPLANATION OF WHY this avoids the "cross-domain" error. But it does, thanks.Overburdensome
double smart karryUltan
@Overburdensome "Most people end their statements with ;" Using semicolons is not recommended as per the standards. Semicolons just add more work, look ugly, and the code works perfectly without them. Check github.com/standard/standardObserve
@Observe developer.mozilla.org says "JavaScript applications consist of statements with an appropriate syntax. A single statement may span multiple lines. Multiple statements may occur on a single line if each statement is separated by a semicolon. This isn't a keyword, but a group of keywords." So I'm in the "use always" faction. I didn't even know there was a "never" faction. I assume the latter also believe the earth is flat? (Just kidding -- thanks)Overburdensome
C
-3

I was having the same issue, and for me it worked by simply concatenating https:${image.getAttribute('src')}

Carnify answered 4/9, 2018 at 12:41 Comment(1)
please explain what you didBallentine

© 2022 - 2024 — McMap. All rights reserved.