简体   繁体   中英

Inline encoding of image data on multiple sub-domains

I am attempting to replace all img tags in a webpage with the base64 encoded data contained in the image.

An image that is represented as:

<img src="http://a.example.com" />

would be changed to:

<img src="data:image/png;base64,iVBORw0KGg..." />

The webpage contains images from multiple sub-domains, eg

<img src="http://a.example.com" />
<img src="http://b.example.com" />

On a single sub-domain I can use:

var images = document.getElementsByTagName('img');
for(var i=0; i<images.length; i++){
 var img = new Image();
 img.src = images[i].src;
 // Create canvas tag to represent img
 var canvas = document.createElement("canvas"); 
 canvas.width = img.width; 
 canvas.height = img.height;
 var context = canvas.getContext("2d");
 context.drawImage(img,0,0);

 img.src = canvas.toDataURL("image/png");
 $(images[i]).replaceWith(img);
}

however, when the images are in a sub-domain other than that of the HTML page, I get:

Uncaught Error: SECURITY_ERR: DOM Exception 18

in the console. How do I perform this operation on a page to convert all image references with their respective encoded data?

You are violating the laws of the spec which state that if you use drawImage with an image that is not of the same origin as that of the canvas's document then the orgin-clean flag is set to false. From then onwards you are not allowed to use toDataURL . The spec's full words on the matter are here.

The reason for this security is to prevent something called information leakage. To see why this is bad, consider the following hypothetical situation:

Say you are on a work network and so you have access to internal, private company sites and your (private!) hard-drive from your browser. The private sites might be something like www.internal.myCompany.com and your hard drive would be accessible from urls like file:///C:/SomeOfMyPhotos/ .

Now suppose you visited a website with a hidden canvas and while you were browsing the site that canvas was constantly calling drawImage() onto that canvas with urls that it was guessing might exist. These urls would be things like an image on the private subdomain:

www.internal.myCompany.com/secret/secret-plans.jpg

Or an image on your hard drive:

file:///C:/SomeOfMyPhotos/thatEmbarassingPhoto.png

The malicious site would keep trying different combinations of private-to-you urls until it found one that was actually a file. Then it would draw it to the canvas. Then it would get the imageData from the canvas and send it off to the server.

Voila! They have stolen your secret plans and your embarassing photos, much without your consent.

Now we know that the above scenario is not very probable: After all, secret plans are almost always in PNG format whereas embarassing photos are nearly always in JPG format. But it stands that situations like the above could happen and so the security implications must take this into account.

The security sandbox will not let you call toDataURL on a canvas if it has had an image from another domain drawn on it. The exact logic escapes me, but it exists to prevent you from doing exactly this.

Generally you cannot read image data from other domains . It violates the Cross-Origin Resource Sharing ( CORS ) security model. That said you can get around this issue by having each subdomain use a HTTP access control header (here's an example with node.js ) as well as setting the crossorigin attribute to use-credentials on each image tag. Note that not all browsers support the crossorigin attribute.

Ultimately, both the server and client have to opt-in to get around CORS security for images.

Another work-around entails using a jquery plugin called $.getImageData . Under the covers this plugin uses a third party service called img-to-json and JSONP to get around the security issues. Here's a concrete example of how to use img-to-json:

"//img-to-json.appspot.com/?url=" + escape(img_url) + "&callback=" + callback

Basically, the service takes two parameters: the URL of the image you're downloading (which needs to be URL escaped), and the name of the callback you want executed when the image is done loading. The callback is passed an objected with the data url version of the image as well as some metadata such as the height and width of the image.

Also, note the URL above is an example of a protocol relative URL (ie it begins with // ), which means it'll worked on both http or https pages.

Hope this helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM