Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why CORS on Images with HTML Canvas?

Recently, I spent a bit of time researching a solution to a rather common problem in web development- I was dealing with logos middle-aligned on a transparent background, but having to place text below them, it would then appear as though the amount of whitespace between the text and the image shifts from page to page. After a bit of research, I discovered I could re-align the images bottom-left using a canvas, and the solution worked beautifully... at least until I integrated the solution into our code-base and discovered it was failing with:

"Unable to get image data from canvas because the canvas has been tainted by cross-origin data." (Say what!?)

Looking into it, the offending code was located at the first line of the following function:

getColumn: function (context, x, y, imageHeight) {
    var arr = context.getImageData(x, y, 1, imageHeight).data; //<-- CORS HATES THIS!
    return pvt.normalizeRGBArray(arr)
}

Now, I understand perfectly well what the CORS standard is, and I know the solution to this problem. The server needs to support CORS first. Either the server can set the http header access-control-allow-origin: "*", or allow a developer to set the crossDomain attribute for the image to "anonymous"/"use-credentials". This is all fine and dandy except when you work on the front-end for a big company where convincing the server-lords to change anything related to security is a non-starter conversation.

So really, my question here is what IS the logic behind having this security error occur with images on a canvas? They are frickin' images for crying out loud! It is fine to download them, hot-link to them, use them in memory, but "oh no!" don't manipulate them in any way whatsoever, or CORS throws an error!

If you ask me, the image is not tainted, it's this hairbrained CORS standard that is. Can somebody please explain the logic for why this happens? How could using a canvas to manipulate an image possibly be a security concern?

like image 955
Joshua Dannemann Avatar asked Jan 26 '16 17:01

Joshua Dannemann


People also ask

What is CORS image?

The HTMLImageElement interface's crossOrigin attribute is a string which specifies the Cross-Origin Resource Sharing (CORS) setting to use when retrieving the image.

Does CORS apply to IMG?

The crossorigin attribute specifies that the img element supports CORS. CORS stands for Cross Origin Resource Sharing. CORS is a standard mechanism to retrieve files from a third party domain or server. If specified, the image file request will be sent with or without credentials.

Why are CORS needed?

The CORS mechanism supports secure cross-origin requests and data transfers between browsers and servers. Modern browsers use CORS in APIs such as XMLHttpRequest or Fetch to mitigate the risks of cross-origin HTTP requests.

What is CORS in HTML?

CORS stands for Cross-Origin Resource Sharing, and is a mechanism that allows resources on a web page to be requested from another domain outside their own domain. It defines a way of how a browser and server can interact to determine whether it is safe to allow the cross-origin request.


2 Answers

This protects users from having private data exposed by using images to pull information from remote web sites without permission.

Source: MDN

like image 74
Mike Cluck Avatar answered Oct 05 '22 19:10

Mike Cluck


Sorry, not an answer to the question but ...

FYI: It's not

Either the server can set the http header access-control-allow-origin: "*", or allow a developer to set the crossDomain attribute for the image to "anonymous"/"use-credentials".

BOTH are required.

You need to set crossOrigin because it changes the request the browser makes to the server for the image. But even if you don't set it, and the server sends the CORS headers anyway the browser will still not let you use the image unless you had set crossOrigin.

You can see it in this example, two images, both of which receive CORS headers from the server but the browser only lets one work.

loadAndDrawImage("https://i.imgur.com/fRdrkI1.jpg", "");
loadAndDrawImage("https://i.imgur.com/Vn68XJQ.jpg");

function loadAndDrawImage(url, crossOrigin) {
  const img = new Image();
  img.onload = function() { 
    log("For image", crossOrigin !== undefined ? "WITH" : "without", "crossOrigin set");
    try {
      const ctx = document.createElement("canvas").getContext("2d");
      ctx.drawImage(img, 0, 0);
      ctx.getImageData(0, 0, 1, 1);
      log("canvas still clean:", name);
    } catch (e) {
      error(name, ":", e);
    }
    log(" ");
  };  
  if (crossOrigin !== undefined) {
    img.crossOrigin = crossOrigin;
  }
  img.src = url;
}

function logImpl(color, ...args) {
  const elem = document.createElement("pre");
  elem.textContent = [...args].join(" ");
  elem.style.color = color;
  document.body.appendChild(elem);
}

function log(...args) {
  logImpl("green", ...args);
}

function error(...args) {
  logImpl("red", ...args);
}
pre { margin: 0; }
<div>check headers in devtools</div>

If you check the header you'll see they both received CORS headers but only one image worked.

enter image description here

enter image description here

like image 39
gman Avatar answered Oct 05 '22 20:10

gman