Ajax et XSS : autoriser les requêtes grâce au CORS

Lorsqu’on développe une application RESTful exploitant des webservices disponibles sur un autre domaine, on est rapidement confronté à des questions de sécurité, notamment le cross-site scripting (XSS).

Le problème

Le cas typique dans lequel on rencontre cette problématique est celui d’un site mobile « m.domaine.com » qui récupère des données sur le site desktop « domaine.com ». Toutes les requêtes Ajax partant de « m.domaine.com » vers « domaine.com » seront bloquées par la règle de sécurité Same Origin Policy.

La solution

La solution à cette problématique est le mécanisme du Cross-origin resource sharing (CORS) spécifié par le W3C et utilisable dans Apache et dans la plupart des navigateurs modernes.

La configuration Apache

Le vhost du domaine cible des requêtes (« domaine.com » dans notre exemple) doit spécifier des directives « Access-Control-Allow » :

<VirtualHost *:80> 
    ServerName domaine.com

    Header set Access-Control-Allow-Origin "http://m.domaine.com"
    Header set Access-Control-Allow-Credentials true
    Header set Access-Control-Allow-Methods "POST, GET, OPTIONS, DELETE, PUT"
    Header set Access-Control-Allow-Headers "content-type, accept, set-cookie"
</VirtualHost>

Astuce

Grâce à la directive « Access-Control-Allow-Headers », on peut indiquer quels entêtes HTTP seront transmis. En autorisant « set-cookie », on peut récupérer, sur « m.domaine.com », le cookie d’une session ouverte sur « domaine.com ». Ainsi, on réalise facilement un petit SSO maison !

Les requêtes Ajax

Avec jQuery, la requête Ajax devra contenir le flag withCredentials à true dans les paramètres xhrFields (voir également la doc de XMLHttpRequest pour les autres implémentations).

$.ajax({
    url: 'http://domaine.com',
    xhrFields: {
        withCredentials: true
    }
});

Attention à la subtilité !

On trouve très souvent la syntaxe ci-dessous :

Header set Access-Control-Allow-Origin "*"

Cette directive ne fonctionne pas lorsqu’on passe le flag withCredentials à true dans les paramètres xhrFields de la requête Ajax. Dans ce cas, il faut préciser explicitement le domaine autorisé :

Header set Access-Control-Allow-Origin "http://m.domaine.com"