Références :
Contrôle d’accès HTTP
Cross-Origin Resource Sharing
Les attaques de type cross-site request forgery
Partage de ressources avec requête multi-origines
Il est de nombreuses configurations dans lesquelles les hôtes de l’application et les points de terminaison des services ( ressources protégées (RS) ou API ) se situent sur des serveurs différents. Les applications dont les ressources sont réparties dans le Web (Public Cloud) constituent un bon exemple de cette configuration.
En utilisation Web normale, cette approche multi-hôte ou, plus précisément, "multi-origines" est restreinte pour des raisons de sécurité. Les règles de partage de ressources multi-origines (Cross-Origin Resource Sharing, CORS) permettent de répondre à ce type de requêtes en toute sécurité.
Différents cas de figure
Comme toujours lorsque nous abordons la question de la communication entre applications réparties sur différents domaines, nous devons d’abord distinguer deux cas :
les serveurs de ressources protégées (RS) sont interrogés par des applications clientes identifiées à l’avance ; c’est toujours le cas lorsque les applications et les RS appartiennent à une même organisation ;
les RS n’ont pas une connaissance préalable des applications.
Le premier cas est trivial. L’autorisation des requêtes multi-origines (Cross-Origin Resource Sharing) pourra être donnée, par exemple si le service est publié par un serveur http Apache ou compatible, dans le fichier .htaccess du RS :
##### CORS pour monsite.com :
SetEnvIf Origin "^http(s)?://(.+\.)?monsite\.com$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Seul le deuxième cas mérite notre attention.
Autorisation après validation du jeton d’identité
Dans le deuxième cas (l’application cliente n’est pas connue du RS) notre solution consiste à inscrire le champ Access-Control-Allow-Origin dans la réponse du serveur de ressource (RS) si, et seulement si, le jeton a été validé auprès du serveur d’authentification. Cela semble la bonne méthode, puisque le RS s’en remet au serveur d’authentification pour réaliser toutes les vérifications nécessaires, et n’a donc pas besoin de connaître au préalable le domaine d’origine de la requête [1] [2].
Voici un exemple de fonction d’autorisation en PHP pour SPIP 3.1, dans laquelle le RS valide le jeton par introspection (voir : Validation du jeton d’accès par interrogation du point d’extrémité "resource") :
- /*
- Autorisation avec OAuth Server by DnC
- Auteur : Bertrand degoy
- Copyright (c) 2016 DnC
- */
- function oauth_authorize($accesstoken) {
- // Interroger OAuth Server by DnC
- include_spip('inc/distant');
- // Vérifier le jeton d'identité (ID token)
- $url = "https://oa.dnc.global/introspect.php?token=" .
- $idtoken&requester_ip=$_SERVER['REMOTE_ADDR'];
- $res = recuperer_url($url);
- if ( (int)$res['status'] === 200 ) {
- // Accès HTTP (CORS) : autoriser l'origine
- }
- return true ;
- }
- } else return false;
- }
Sécurité :
Notez que l’IP $_SERVER[’REMOTE_ADDR’] est passée à l’introspection par le paramètre ’requester_ip’ et sera comparée à l’IP de l’application cliente enregistrée sur le serveur. Il n’y a donc pas de risque de répondre à une application étrangère. Voyez : Vérification de l’origine de la requête reçue par un serveur de ressource.
Tester :
http://chemindeleau.com/?page=test-rpc-3 (utilise la fonction ProtectedApi_GET_auto() décrite ici : Une fonction pour tout simplifier !).
Cet exemple s’appuie sur l’API HTTP REST de Radar. Le site Chemin de l’eau est un site opérationnel qui met en œuvre des fonctionnalités issues des prototypes OAuthSD et Radar.
Message d’erreur : Blocage d’une requête multi-origines
"Blocage d’une requête multi-origines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante située sur http://r.dnc.global/http.api/collectionjson/gis?lat=47.8655638&lon=5.3314162&dmax=10&token=. Raison : échec du canal de pré-vérification des requêtes CORS."
La requête suivante, bien que le type indiqué soit ’GET’, est négociée avec la méthode ’OPTIONS’. Il en résulte que la requête cross-site est de type requête prévérifiée ("preflighted requests") :
- <div id="result"></div>
- <script>
- $.ajax({
- url: 'http://r.dnc.global/http.api/collectionjson/gis',
- dataType: 'json',
- type: 'GET',
- contentType: 'application/json',
- data: 'lat=47.8655638&lon=5.3314162&dmax=10&token=#SESSION{auth_token}',
- timeout: 15000,
- success: function(data) {
- var text = '<b>Requete : ' + data.collection.href + '</b><br /><br />';
- items = data.collection.items;
- for (var i=0; i < items.length; i++ ) {
- for (var j=0; j < items[i].data.length; j++ ) {
- text += items[i].data[j].name + '=' + items[i].data[j].value + "<br />";
- }
- text += "<br />";
- }
- $("#result").html(text);
- },
- error: function(oops) {
- // oops.statusText returns error
- $("#result").html(oops.statusText);
- }
- });
- </script>
Cliquez sur la vignette pour voir la requête et la réponse :
(Article à compléter. Mais est-ce pertinent ? L’utilisation de script expose le jeton, ce n’est pas la méthode recommandée).