22

I'm loading a motion jpeg from third-party site, which I can trust. I'm trying to getImageData() but the browser (Chrome 23.0) complains that:

Unable to get image data from canvas because the canvas has been tainted by
cross-origin data.

There are some similar questions on SO, but they are using local file and I'm using third party media. My script runs on a shared server and I don't own the remote server.

I tried img.crossOrigin = 'Anonymous' or img.crossOrigin = '' (see this post on the Chromium blog about CORS), but it didn't help. Any idea on how can I getImageData on a canvas with cross-origin data? Thanks!

fab
  • 317
  • 4
  • 20
clwen
  • 20,004
  • 31
  • 77
  • 94
  • 4
    Maybe you trust that 3rd party website, but there is no way for the user to tell that the 3rd party website trusts you. Your script could extract an image from a 3rd party website which is only accessible to the user and not accessible to you (maybe because it's only available to registred users and the user has logged in). The script could then forward that imagedata to your own server and give you illicit access. – Philipp Dec 23 '13 at 05:38

4 Answers4

14

You cannot reset the crossOrigin flag once it is tainted, but if you know before hand what the image is you can convert it to a data url, see Drawing an image from a data URL to a canvas

But no, you cannot and should not be using getImageData() from external sources that don't support CORS

Community
  • 1
  • 1
Paul Kaplan
  • 2,885
  • 21
  • 25
  • 1
    Thanks for replying. Not sure we can apply `toDataURL()` to mjpeg, but I'll give it a try. Why no? security reason? Is there specific configuration external server should do? Or even they don't have that configuration, I can use toDataURL to do so. – clwen Dec 04 '12 at 16:52
  • Here is a fairly comprehensive guide on CORS support http://www.html5rocks.com/en/tutorials/cors/. And yeah it is a large security issue. – Paul Kaplan Dec 04 '12 at 17:02
  • 3
    Can this work even with CORS? I have not had success with it. – Meekohi Jan 22 '13 at 16:27
  • How do I know *before hand*? Imagine you've got an `` tag and you have no idea what it is... How do you know if you can draw it or if you need to load it using `crossorigin="anonymous"`? – Tomáš Zato Dec 01 '14 at 21:43
  • @TomášZato You dont, because that is exactly what CORS is trying to prevent. It should only be allow *IF* you know before hand what the image is and if it is safe. – gbtimmon Sep 04 '18 at 15:14
  • Here's another option: you can cache the image with [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache/add) – tmpmachine Oct 19 '20 at 07:32
11

While the question is very old the problem remains and there is little on the web to solve it. I came up with a solution I want to share:

You can use the image (or video) without the crossorigin attribute set first and test if you can get a HEAD request thru to the same resource via AJAX. If that fails, you cannot use the resource. if it succeeds you can add the attribute and re-set the source of the image/video with a timestamp attached which reloads it.

This workaround allows you to show your resource to the user and simply hide some functions if CORS is not supported.

HTML:

<img id="testImage" src="path/to/image.png?_t=1234">

JavaScript:

var target = $("#testImage")[0];
    currentSrcUrl = target.src.split("_t=").join("_t=1"); // add a leading 1 to the ts
$.ajax({
    url: currentSrcUrl,
    type:'HEAD',
    withCredentials: true
})
.done(function() {
    // things worked out, we can add the CORS attribute and reset the source
    target.crossOrigin = "anonymous";
    target.src = currentSrcUrl;
    console.warn("Download enabled - CORS Headers present or not required");
    /* show make-image-out-of-canvas-functions here */
})
.fail(function() {
    console.warn("Download disabled - CORS Headers missing");
    /* ... or hide make-image-out-of-canvas-functions here */
});

Tested and working in IE10+11 and current Chrome 31, FF25, Safari 6 (Desktop). In IE10 and FF you might encounter a problem if and only if you try to access http-files from a https-script. I don't know about a workaround for that yet.

UPDATE Jan 2014:

The required CORS headers for this should be as follows (Apache config syntax):

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "referer, range, accept-encoding, x-requested-with"

the x-header is required for the ajax request only. It's not used by all but by most browsers as far as I can tell

Jörn Berkefeld
  • 2,540
  • 20
  • 31
  • Thanks for replying. So for "working" you meant that one could do `getImageData` on this external resource? If we do the workaround as you mentioned. – clwen Dec 22 '13 at 21:12
  • Old answer, but just to note that the other way around seems easier : first set the crossOrigin of your img tag, listen to its `error` event handler, set the remote src, if it fires, you know you'll taint the canvas. – Kaiido Nov 22 '16 at 12:58
6

Also worth noting that the CORS will apply if you are working locally regardless of if the resource is in the same directory as the index.html file you are working with. For me this mean the CORS problems disappeared when I uploaded it to my server, since that has a domain.

Kristopher Ives
  • 5,838
  • 7
  • 42
  • 67
4

You can use base64 of the image on canvas, While converting into base64 you can use a proxy URL (https://cors-anywhere.herokuapp.com/) before your image path to avoid cross-origin issue

check full details here

https://stackoverflow.com/a/44199382/5172571

var getDataUri = function (targetUrl, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function () {
        var reader = new FileReader();
        reader.onloadend = function () {
            callback(reader.result);
        };
        reader.readAsDataURL(xhr.response);
    };
    var proxyUrl = 'https://cors-anywhere.herokuapp.com/';
    xhr.open('GET', proxyUrl + targetUrl);
    xhr.responseType = 'blob';
    xhr.send();
};
getDataUri(path, function (base64) {
    // base64 availlable here
})
Manish Mittal
  • 207
  • 2
  • 10