Using Cross-domain images in WebGL and Chrome 13

Wednesday, July 06, 2011

Labels:

A few weeks ago, we became aware of a security issue with WebGL: shaders could be used to indirectly deduce the contents of textures uploaded to the GPU. As a result, the WebGL specification was updated to be more restrictive when it comes to using cross-domain images and videos as WebGL textures.

As a result, Chrome 13 (and Firefox 5) will no longer allow cross-domain media as a WebGL texture. The default behavior will be a DOM_SECURITY_ERR. However, applications may still utilize images and videos from another domain with the cooperation of the server hosting the media, otherwise known as CORS.

CORS support for MediaElements has also been fully implemented in WebKit by setting a new .crossOrigin attribute. This means that sophisticated applications that were using cross-origin textures before, can continue to do so, assuming the hosting image server grants the necessary cross-origin permission using CORS. If you want to enable CORS request on an image, all you have to do is add one line of code:

var img = document.createElement('img');
img.onload = function(e) { … };
img.crossOrigin = ''; // no credentials flag. Same as img.crossOrigin='anonymous'
img.src = 'http://other-domain.com/image.jpg';


Another nice property that we gain from this new setting is the ability to read cross-domain image data set on a 2D canvas. Normally, filling a canvas with a remote image (e.g. ctx.drawImage()) flips the origin-clean flag to false. Attempting to read back the pixels using ctx.toDataURL() or ctx.getImageData() throws a SECURITY_ERR. This is to prevent information leakage. However, when .crossOrigin is set (and the remote server supports CORS), the read is possible. For example:

var img = document.createElement('img');
img.onload = function(e) {
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
var url = canvas.toDataURL(); // Read succeeds, canvas won't be dirty.
};
img.crossOrigin = '';

img.src = 'http://other-domain.com/image.jpg';


Unfortunately, this new restriction in WebGL means that some existing content will break. We’ve already started working with external image and video hosting services like Flickr to evangelize the use of CORS on their images.

You can test this new behavior today using images from Picasa, which already sends a CORS header allowing cross-origin requests, and the Chrome dev channel.

4 comments:

Eli said...

I don't understand the point of the HTMLImageElement.crossOrigin property. CORS information is sent in the Access-Control headers. What should a property on the image element have to do with whether or not the CORS information is taken in account? This is akin to setting an XMLHttpReuqest.yeahImSureIWantToUseCORS property when doing a cross-domain XHR. It makes no sense.

This is not how the CORS spec was intended to work. You shouldn't have to opt-in.

Eli said...

This comment has been removed by the author.

Kenneth Russell said...

The crossOrigin property changes how the HTTP request for the image is sent (in particular, without cookies), which is what makes the new behavior secure. See this Chromium issue.

nali said...

Great!
Hopefully you are also talking with storage cloud providers about the importance of CORS.
I would highly appreciate if you could name some of those already implementing it.