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.”
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).
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.
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.)
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>
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); ?>
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):
This content is protected by a licence
Creative Commons Attribution-ShareAlike 2.0 France "BY-SA (FR)"