Un livre d'or qui exécute les messages
Un restaurant ajoute un livre d'or à son site. Les clients laissent un avis, et tout le monde lit les avis sur une même page.
Un visiteur écrit un avis qui n'est pas fait de mots, mais d'un minuscule programme. Chaque visiteur suivant ouvre la page, et son navigateur l'exécute sans rien dire. Le restaurant n'a rien écrit de tout cela.
La règle unique
Attention
Ce laboratoire est un site volontairement fragile. Attaquez votre copie privée, et rien d'autre : elle n'a aucune sortie vers Internet. Attaquer un système qui n'est pas le vôtre, ou sans permission de test, est un délit dans presque tous les pays.
À la fin de cette leçon
Remarque
Je peux empêcher une page d'exécuter un texte tapé par un visiteur.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Script intersite | une page qui exécute un script tapé par un visiteur | XSS |
| Script | un petit programme que le navigateur exécute dans une page | |
| Échapper | remplacer les caractères spéciaux par un texte sans danger | < devient < |
| Réfléchi | envoyé dans la requête et renvoyé aussitôt dans la réponse | |
| Stocké | enregistré par le site et montré à tous les visiteurs suivants | |
| Cookie | une petite valeur que le navigateur garde et renvoie au site |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Un texte tapé peut devenir la page
Un navigateur lit une page comme du texte avec des balises. La balise <b> met en gras. La balise <script> lance un programme.
Si un site met le texte d'un visiteur dans la page sans le changer, le navigateur ne distingue plus ce texte des balises du site. Le script tapé s'exécute comme si le site l'avait écrit.
Exemple travaillé : réfléchi et stocké
| Réfléchi | Stocké | |
|---|---|---|
| Où va le texte | Dans la requête, puis aussitôt dans la réponse | Dans les données du site, puis dans les pages suivantes |
| Qui est touché | Celui qui ouvre le lien piégé | Tous ceux qui ouvrent la page ensuite |
| Exemple | Une page de recherche qui répète : vous avez cherché ... | Un livre d'or ou un avis sur un produit |
| Pourquoi stocké est pire | Un seul message touche beaucoup de visiteurs, sans lien à envoyer |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Regardez le site de votre laboratoire
Votre laboratoire fait tourner le site que vous allez tester. Démarrez-le, puis lancez ces trois commandes. Elles regardent seulement la page de connexion. Elles ne se connectent pas et ne tapent aucun script.
Les cookies comptent ici. Un script qui s'exécute dans une page peut lire ses cookies, sauf si le cookie porte la marque HttpOnly.
Le site répond-il ?
curl -s -o /dev/null -w "%{http_code}\n" http://dvwa/login.phpAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Les cookies posés par le site
curl -s -I http://dvwa/login.php | grep -i "^set-cookie"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Combien de lignes disent HttpOnly ?
curl -s -I http://dvwa/login.php | grep -ic httponlyAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce que disent les trois réponses
La première affiche 200. La deuxième affiche deux lignes : un cookie nommé security et un cookie PHPSESSID, qui est la session. La troisième affiche 0.
Aucun cookie ne porte HttpOnly. Un script exécuté dans une page de ce site pourrait donc lire le cookie de session. C'est pourquoi un XSS mène souvent au vol de session.
Échapper transforme les caractères spéciaux
Le remède est d'échapper le texte du visiteur quand il entre dans la page. < devient < et > devient >. Le navigateur les dessine comme les mêmes signes, mais ne les lit jamais comme une balise.
Remplacez d'abord &. Si vous le remplacez en dernier, vous changez le & de < en & et le texte sort faux.
Une fonction d'échappement
def echapper(texte):
texte = texte.replace("&", "&")
texte = texte.replace("<", "<")
texte = texte.replace(">", ">")
return texte
# Changez l'avis et lancez le code. Puis mettez la ligne du & en dernier et relancez.
avis = "Bon pain <b>et</b> gâteaux & thé"
print("<p>" + echapper(avis) + "</p>")
Une erreur fréquente
Attention
Beaucoup pensent qu'il suffit de refuser le mot script quand on enregistre un avis. C'est faux : d'autres balises, comme img, portent aussi du script, et un filtre oublie des écritures. Échappez au moment où vous écrivez le texte dans la page, pour chaque valeur : la question ne se pose plus.
Échapper à la sortie, puis ajouter des couches
Échappez quand vous écrivez le texte dans une page, pas quand vous l'enregistrez. Le même texte ira peut-être dans une page, un courriel ou un fichier, et chacun veut son échappement.
Ajoutez des couches derrière. Marquez les cookies de session HttpOnly. Envoyez un en-tête Content-Security-Policy qui limite les scripts permis. Elles réduisent les dégâts si un échappement est oublié.