By Alexandre Alapetite on 2008-03-22; updated on 2009-01-16
français

Mini cross-site requests using the dimensions of an image

Because of security implications, it is by default impossible in JavaScript in a Web page client side (e.g. Ajax) to access a resource from another server (domain) than the one that served the current Web page (see XSS).

However, there are many legitimate cases where this possibility would be useful. This is why an Access Control for Cross-site Requests is being standardised (and implemented by e.g. Mozilla Firefox 3).

In the mean time, for the current and former versions of Web browsers, there is a real need. A few propositions have been made using techniques based on e.g. iframe (working well for sub-domains of the same domain), but as far as I know, none of them allows clean and standard access to information hosted on another domain.

Therefore, I propose here a simple, robust and standard-compliant technique based on reading the width and height attributes of an <img> element containing a dynamic image generated on a foreign server.

The information returned is limited to two integers, admittedly, but this is already enough to cover a number of needs.

My use case was to get access from the client to another server on its local network that was not reachable from the original server.


Concept

When an image is loaded (in HTML or in JavaScript), even though it comes from a foreign domain, it is still possible to read its dimensions via the width and height attributes.

Since this image can be dynamically generated by the foreign server, those two attributes are enough to exchange some minimal data cross-site, if both sites can agree in advance about the meaning given to the numerical values of width and height:

http://example.org/test.html

<html>
...
<img id="xss" src="http://example.net/image.png.php?parameter=value" alt="[Cross-site]" />
...
<script type="text/javascript">
	var xss = document.getElementById('xss');
	alert(xss.width + ';' + xss.height);
</script>
</html>

In practice, this concept will be used by building a JavaScript image object instead of using the <img> tag, as exemplified below.

Note: The attributes naturalwidth and naturalHeight can alternatively be used to be sure that the image natural dimensions are not modified client side.

This technique has been tested successfully, even on older browsers such as Microsoft Internet Explorer 5 (1999), Netscape 6 [Mozilla, Gecko] (2000), Opera 7 (2003).


About the image’s weight

The dimensions of the picture should not be too large, to avoid using too heavy objects. Nevertheless, a monochrome PNG can be compressed very efficiently. Here is for instance a plain PNG of 8192×8192 pixels that weights only 8 KB. The time needed to generate large images can furthermore become too long; if this happens to be the case, a library of pre-compressed images can be used.


Example

In this simple example, I will show how to retrieve the current time from a foreign server using an image which width codes for the hours (+1 to avoid 0), and which height codes for the minutes (+1). (It could be any other type of information.)

Client side, served from the current server

http://example.org/test.html

<script type="text/javascript">
	var foreignTime = new Image();
	foreignTime.onload = function(e)
	{//Execute the code when the image is loaded
		alert('The foreign time given by alex.alapetite.fr is ' +
			(10 > foreignTime.width ? '0' : '') + (foreignTime.width - 1) + ':' +
			(10 > foreignTime.height ? '0' : '') + (foreignTime.height - 1));
	}
	//The random parameter is there to avoid caching issues
	foreignTime.src = 'http://example.net/test.png.php?r=' + Math.random();
</script>

Server side, foreign server

http://example.net/test.png.php

<?php
header('Content-Type: image/png');
//For future browsers: http://www.w3.org/TR/access-control/
header('Access-Control-Allow-Origin: *');
$now = getdate();
//The dimensions of the image are function of the time
$image = imagecreate($now['hours'] + 1, $now['minutes'] + 1);
$background = imagecolorallocate($image, 0, 0, 0);
imagepng($image);
imagedestroy($image);
?>

Live demonstration

To make the demonstration more realistic, you should try to run the JavaScript from a Web page served by your own server.
Nevertheless, here is a demo using http://alex.alapetite.fr/doc-alex/xss-img/time.png.php, which is not the same domain as the current Web page (never mind the redirection):


Licence

This content is protected by a licence Creative Commons Attribution-ShareAlike 2.0 France "BY-SA (FR)" [Creative Commons License]


Comments

Object: View comments

https://alexandre.alapetite.fr

Back