Par Alexandre Alapetite le 2008-03-22 ; mise à jour 2009-01-16
English

Mini requêtes inter-domaines grâce aux dimensions d’une image

Pour des raisons de sécurité, il est par défaut impossible en JavaScript dans une page Web côté client (ex : Ajax) d’accéder à une ressource d’un autre serveur (domaine) que celui qui a servi la page Web courante (voir XSS).

Néanmoins, il y a de nombreuses situations légitimes où de telles requêtes inter-domaines seraient utiles. C’est pourquoi un contrôle d’accès pour les requêtes inter-domaines est en cours de standardisation (et implémenté entre autres par Mozilla Firefox 3).

En attendant, pour les navigateurs actuels et passés, il y a un réel besoin. Un certain nombre de propositions ont été faites, basées par exemple sur iframe (ce qui marche bien pour des sous-domaines d’un même domaine), mais autant que je sache, aucune ne permet un accès propre et standard à une information hébergée sur un autre domaine.

En conséquence, je propose ici une méthode simple, robuste et respectant les standards, basée sur la lecture des attributs width et height d’un élément <img> contenant une image générée dynamiquement depuis un serveur étranger.

L’information retournée est certes limitée à deux nombres entiers, mais cela suffit déjà à couvrir un certain nombre de besoins.

« Mon cas d’utilisation était l’accès depuis le client à un autre serveur sur son réseau local qui n’était pas joignable depuis le serveur d’origine. »


Concept

Lorsque l’image est chargée (en HTML ou en JavaScript), même lorsqu’elle provient d’un serveur étranger, il est toujours possible de lire ses dimensions via les attributs width (largeur) and height (hauteur).

Comme cette image peut être générée dynamiquement depuis le serveur étranger, ces attributs suffisent à échanger un minimum d’information inter-domaine, si les deux sites se sont mis d’accord à l’avance sur la signification des valeurs numériques de width et height :

http://example.org/test.html

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

En pratique, ce concept sera plutôt utilisé en construisant un objet image en JavaScript au lieu de la balise <img>, comme illustré ci-dessous.

Note : Les attributs naturalwidth et naturalHeight peuvent alternativement être utilisés pour être sûr que les dimensions naturelles de l’image ne sont pas altérées côté client.

Cette technique a été testée avec succès même sur des navigateurs anciens comme Microsoft Internet Explorer 5 (1999), Netscape 6 [Mozilla, Gecko] (2000), Opera 7 (2003).


À propos du poids de l’image

Les dimensions de l’image ne devraient pas être trop importantes, pour éviter d’utiliser des objets trop lourds. Néanmoins, un PNG monochrome se compresse très bien. Voici par exemple une image PNG unie de 8192×8192 pixels qui fait seulement 8 Ko. Le temps de génération dynamique de larges images peut par ailleurs devenir trop long ; si tel est le cas, une bibliothèque d’images pré-compressées peut être utilisée.


Exemple

Dans ce simple exemple, je vais montrer comment récupérer l’heure courante d’un serveur étranger en utilisant une image dont la largeur représente les heures (+1 pour éviter 0), et dont la largeur représente les minutes (+1). (Cela pourrait être n’importe quel autre type d’information.)

Côté client, servi depuis le serveur courant

http://example.org/test.html

<script type="text/javascript">
	var tempsEtranger = new Image();
	tempsEtranger.onload = function(e)
	{//Exécute le code lorsque l’image est chargée
		alert('L’heure donnée par alex.alapetite.fr est ' +
			(10 > tempsEtranger.width ? '0' : '') + (tempsEtranger.width - 1) + 'h' +
			(10 > tempsEtranger.height ? '0' : '') + (tempsEtranger.height - 1));
	}
	//Le paramètre random permet de contourner certains problèmes de cache
	tempsEtranger.src = 'http://example.net/test.png.php?r=' + Math.random();
</script>

Côté serveur, serveur étranger

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

<?php
header('Content-Type: image/png');
//Pour les futurs navigateurs : http://www.w3.org/TR/access-control/
header('Access-Control-Allow-Origin: *');
$now = getdate();
//Les dimensions de l’image sont fonction de l’heure
$image = imagecreate($now['hours'] + 1, $now['minutes'] + 1);
$background = imagecolorallocate($image, 0, 0, 0);
imagepng($image);
imagedestroy($image);
?>

Démonstration en ligne

Pour une démonstration plus réaliste, vous pouvez exécuter le JavaScript depuis une page Web sur votre propre serveur.
Néanmoins, voici une démo utilisant http://alex.alapetite.fr/doc-alex/xss-img/time.png.php, qui n’est pas le même domaine que la page Web courante (ne tenez pas compte de la redirection) :


Licence

Ce contenu est protégé par une licence Creative Commons Paternité - Partage des Conditions Initiales à l’Identique 2.0 France "BY-SA (FR)" [Creative Commons License]


Commentaires

object : Voir les commentaires

http://alexandre.alapetite.fr

Retour