Envie de by-passer la Same Origin Policy ?

le 30/10/2008 par Olivier Mallassi
Tags: Software Engineering

L'agrégation d'IHM ! ou nouveau buzz, les mashups ! Bref, ce besoin d'agréger dans le navigateur des informations provenant de sites multiples peut se trouver freiné par une règle de sécurité séculaire (enfin depuis Netscape 2.0...) : la Same Origine Policy.

La Same Origin Policy a pris naissance avec Netscape 2.0 et vise à empêcher l'accès - en lecture ou en écriture - à des serveurs dont le nom de domaine est différent de celui dont provient le document, la page en cours...Derrière cette limitation, se cache l'idée de confiance et la volonté d'empêcher un document d'un domaine A de voir son intégrité modifiée par un document d'un domaine B.

La Same Origin Policy induit donc la notion de domaine, et donc que deux domaines seront identiques à la condition qu'ils aient le même protocole (http, https...), le même " host " et le même port.

Il y a cependant quelques passe-droits à cette " policy " que sont les iframe et l'accès à des ressources de type JavaScript. Autrement dit, il est tout à fait possible depuis une ressource d'un domaine www.myhost.com:80 de référencer une ressource JavaScript du domaine www.myhost.com:7777. Comme illustrer sur le schéma suivant, la ressource file.js peut être référencée et utilisée. En revanche, il est impossible de faire d'accéder à cette ressource depuis un objet de type XmlHttpRequest.

C'est même sur ce principe que reposent certaines optimisations de distribution des ressources. Un nom de domaine pour les images, un nom de domaines pour les css...

"Same Origin Policy" et Flex

Flex propose sa solution pour contourner la Same Origin Policy : un fichier domain.xml qui permet de définir quels sont les domaines autorisés à requêter le domaine ayant le fichier en question...Compliqué ? pas tant que ça.

Imaginons deux domaines : www.company.com qui servirait le fichier swf et services.company.com qui proposerait une API quelconque, là n'est pas le débat.
Par défaut, les requêtes émises depuis le client (donc la ressource swf associée au domaine www.company.com) ne sont pas autorisées à requêter le domaine services.company.com.

Considérons maintenant l'exemple de fichier domain.xml suivant :

< ?xml version="1.0"?>
   < !DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
   <cross -domain-policy>
      <allow -access-from domain="*.company.com" />
   </cross>

Ce fichier, une fois déposé sur le serveur au domaine services.company.com autorise les requêtes de toutes ressources provenant des domaines de type *.company.com et a fortiori www.company.com.

Autrement dit, ce fameux fichier doit être mis à disposition sur les serveurs pouvant être accédés par d'autres domaines et non pas l'inverse.

Same Origin Policy et JavaScript

En technologie JavaScript, rien n'est nativement prévu et il est de prime abord impossible de faire une requête http vers un domaine, un host différent du document en cours. Le lecteur averti (je l'adore celle là ;-) ) aura remarqué l'utilisation de " prime abord ".

Néanmoins et comme présenté plus haut, la " Same Origin Policy " n'empêche absolument pas de référencer des urls de domaines différents dans une balise

script. Et c'est là que ca devient génial !Prenez le code suivant (attention aux espaces superflus) :

<html>
    <head>
        <script src="lib/prototype/prototype.js" type="text/javascript">
        </script>
    </head>
    <body>
        <h3>Same Origin Policy Sample</h3>
        <script> //balise script #1
            function processIt(data){
                for (var i = 0; i < data.responseData.results.length; i++) {
                    document.write(data.responseData.results[i].title);
                    document.write(data.responseData.results[i].visibleUrl);
                    document.write("<br/>");
                }
            }
        </script>
        <script src="http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=Octo%20Technology&callback=processIt"> //balise script #2
        </script>
    </body>
</html>

La première balise script parse le résultat d'un objet " data " qui est un tableau. La seconde balise script référence une url google qui dans un navigateur retourne :

processIt({"responseData":   {"results":[{"GsearchResultClass":"GwebSearch","unescapedUrl":"http://www.octo.com/","url":"http://www.octo.com/","visibleUrl":"www.octo.com","cacheUrl":"http://www.google.com/search?  qu003dcache:8jNCWXNMhgEJ:www.octo.com","title":"u003cbu003eOCTO Technologyu003c/bu003e - Cabinet  du0026#39;architectes en Systèmes du0026#39;Information","………

Le truc intéressant avec les données retournées, c'est leur format : une méthode processIt - qui prend le nom définit dans le paramètre callback de l'url - qui prend en paramètre un tableau complexe au format JSON.

A l'exécution, le navigateur interprète la seconde balise script et récupère la ressource référencée dans l'attribut src, ie. le flux proposant une méthode avec en paramètre un tableau de valeurs. Ainsi la seconde balise script devient un simple appel à la méthode processResult, elle-même définie dans le document associé au domain " 127.0.0.1 ", avec des paramètres particuliers...

Au final, la page suivante s'affiche (vous aurez noté le haut travail de design ;-) ) :

L'autre point intéressant est les requêtes envoyées par le navigateur.

On note bien que le chargement du document depuis le domain " 127.0.0.1 " réalise un GET sur une ressource du domaine ajax.google.com.

Pour conclure...

Contourner la " same origin policy " est donc possible de façon assez simple et permet d'adresser une partie des problématiques de mashup. L'exemple précédent, bien que simpliste, permet sans problème d'imaginer de modifier dynamiquement - ie via JavaScript - la seconde balise script - ie. le DOM - et notamment la valeur de l'attribut src. Il serait ainsi possible de réagir à d'autres interactions d'un utilisateur, ailleurs dans le document...

Reste néanmoins à savoir si le contournement de cette policy est la meilleure idée que vous ayez eu jusqu'à présent car comme dans toute zone de confiance, il vous faudra être certain de la qualité des flux JSON échangés...