Makina Blog
Sécurité Web: Utiliser du mauvais HTML pour s'évader d'un DIV
Cassez une mise en page HTML avec du mauvais HTML et l'aide des navigateurs.
version Française (English version available on regilero's blog). temps de lecture estimé: 15 min.
Pourquoi?
Ne vous êtes vous jamais demandés pourquoi vous ne pouviez pas entrer du HTML sur facebook? Cela semble si facile sur la plupart des sites webs de contribuer avec un sous-ensemble du HTML, pourquoi est-ce qu'ils ne l'autorisent pas?
Ou bien peut-être saviez-vous que ceci était possible sur les "Notes Facebook", avant Mai 2014,vous aviez un petit éditeur de texte riche où il était possible d'entrer quelques tags HTML (en mode wysiwyg ou source). Mais, ce n'est plus possible, le mode wysiwyg est maintenant obligatoire, et même en utilisant l'API vous verrez que les toutes petites parties autorisées du HTML sont toujours très très propres. Très peu de niveaux d'imbrication, politique très stricte de fermeture et d'ouverture de balises, pas d'attributs, etc.
Et bien, dans cet article je vais essayer de vous expliquer pourquoi. Je vais vous montrer comment, en jouant très simplement avec une syntaxe HTML erronée sur un tout petit sous-ensemble du HTML, il est très facile pour du HTML contribué de s'échapper de la boîte et d'écrire un peu partout sur la page.
Ceci m'a permis de décrocher un bounty facebook (prime) de 1500 $US (et un T-shirt github). Donc, oui, c'est très très simple mais cela reste très ennuyeux et très réel.
J'aime bien regarder les choses très simples, et s'échapper d'une boîte HTML est une chose très basique. Tellement simple que la plupart des personnes ne voient pas où est le problème.
Donc, avant de lire, pensez aux contributions HTML et à l'injection de HTML. On ne parle pas de XSS ou d'injection SQL. le problème ici est le suivant:
- Pouvez-vous autoriser un sous-ensemble du HTML que l'utilisateur pourra utiliser pour contribuer à une petite partie de votre mise en page?
- Pouvez-vous assurer que le contenu contribué restera dans sa boîte, dans la partie dédiée du sous ensemble de la mise en page.
Ceci impacte tous les langages de markup dont la destination finale est le HTML (comme le HTML partiel, bien sur, mais aussi Markdown, REST, etc.).
Et donc?
le HTML implicite et les navigateurs qui corrigent vos erreurs
Quand vous nourrissez votre navigateur préféré avec un peu de HTML, le navigateur est très gentil et va essayer de fixer un grand nombre d'erreurs qui pourraient être présentes dans la page.
Ce comportement est un élément très important du succès du web. si les navigateurs avaient générés des pages blanches de la mort (WSOD) à chaque erreur de syntaxe HTML, le web serait beaucoup plus petit.
C'est très bien pour l'utilisateur final.
C'est très bien pour le développeur web.
C'est mauvais en terme de sécurité.
Une des corrections apportées par le navigateur est la correction du HTML en ajoutant des balises fermantes quand la page a visiblement oublié certaines de ces balises.
Donc, ceci :
<!-- initial -->
<div class="foo">
<ul>
<li>foo
<li>bar
</ul>
</div>
Est automatiquement réécrit avec un </LI>
fermant :
<!-- rendered -->
<div class="foo">
<ul>
<li>foo</LI>
<li>bar</LI>
</ul>
</div>
Mais regardons quelque chose de plus méchant, nous avons un <DIV>
ouvrant au sein d'un <LI>
:
<!-- initial -->
<div class="foo">
<ul>
<li>foo
<div class="second">
test
</li>
<li>bar<li>
</ul>
</div>
Le navigateur va ajouter un DIV fermant :
<!-- rendered -->
<div class="foo">
<ul>
<li>foo
<div class="second">
test
</DIV> <!-- implicit HTML -->
</li>
<li>bar<li>
</ul>
</div>
Il y a des chances qu'un filtre de sortie serveur (server side output filter), filtrant les contributions HTML aurait détecté ce HTML comme invalide. si vous comptez les balises ouvrantes et fermantes vous voyez qu'une balise fermante est manquante.
Donc notre mauvais contributeur va plutôt écrire ceci :
<!-- initial -->
<div class="foo">
<ul>
<li>foo<div class="second"><div class="third">test</li>
<li>bar<li>
</ul>
</div> <!-- end third -->
</div> <!-- end second -->
</div>
Et si vous comptez le nombre de balises ouvrantes et fermantes, ou bien si vous utilisez des regex (n'êtes-vous pas complètement fou?), il y a des chances que ce contenu semble valide.
Et maintenant le résultat dans le navigateur :
<!-- rendered -->
<div class="foo">
<ul>
<li>foo
<div class="second">
<div class="third">test
</DIV> <!-- implicit HTML - end third-->
</DIV> <!-- implicit HTML - end second -->
</li>
<li>bar<li>
</ul>
</div> <!-- end third? no, end foo -->
</div> <!-- end second? no end foo's parent if any -->
</div> <!-- end foo's parent's parent if any -->
Deux </DIV>
fermants sont automatiquement et implicitement ajoutés par le navigateur, parce que vous n'avez pas le droit de fermer la balise <LI>
sans fermer le <DIV>
qui est à l'intérieur. Dorénavant, dans la page finale, vous avez deux balises fermantes en extra.
D'accord, trop de divs fermants, et après?
Si le contributeur peut entrer <div>
et </div>
, et si vous comptez le nombre de balises ouvrantes et fermantes pour validez que le HTML est correct, vous avez tort.
le HTML ne sera correct que si l'ordre des balises ouvrantes et fermantes est correct, si certaines balises sont fermées avant d'autres cela risque de casser la mise en page (cela dépend des balises).
Pour le contributeur, ceci signifie écrire en dehors de la boîte contrôlée (la zone des commentaires utilisateur par exemple) et la possibilité d'ajouter du contenu, avec le sous ensemble du HTML dont il dispose, sur d'autres parties de la page (peut-être jouera-t-il avec <table>
et <br/>
pour fabriquer un contenu imitant le contenu attendu, un contenu que les utilisateurs jugeront certainement plus sûr qu'un commentaire de spam -- voir les derniers exemples à la fin de ce texte).
Démo
Voici quelques iframes qui utilisent la fermeture implicite de DIV, et qui écrivent simplement une ligne de texte dans le div main, au lieu de l'écrire au sein des 3 DIV imbriqués dédiés à ce texte.
Une chose intéressante à faire est d'ouvrir une des ces iframes dans une nouvelle fenêtre et de regarder la source. Voici un exemple pour la première iframe, où nous voyons en rouge que quelque chose a été détecté comme invalide.
En inspectant le DOM, on peut voir les DIV corrigées.
Ici:
- le but est d'écrire quelque chose en rouge, dans le div main
- certains tests devraient être en échec (utiliser
<P>
ou<TD>
<TR>
ne permet pas de casser les divs de la même manière que pour la plupart des balises inline comme<span>
,<strong>
,<sub>
, etc.)
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link | direct link |
direct link |
H1, H2, H3, PRE, LI
Ce sont les balises que je préfère.
Parce qu'il y a de fortes chances qu'elles fassent partie des balises autorisées pour les contributeurs.
Est-ce que ça peut casser <section>
ou <article>
?
Oui.
Déjà, si vous autorisez <section>
ou <article>
dans les balises autorisées pour les contributeurs, cela va va automatiquement permettre au contributeur de les fermer. Faire cela serait une erreur.
Plus généralement, si vous utilisez un <DIV>
dans la mise en page (le layout), en fermant cette balise on ferme aussi toutes les balises <section>
ou <article>
à l'intérieur de ce div.
Regardons cela dans une mise en page plus complexe :
Voici une iframe avec une mise en page complète (si on veut), on y voit un DIV encadrant la balise article.
Et voici cette mise en page après un mauvais commentaire qui ferme les commentaires et le div main (les sections aside et footer réelles sont visibles très loin dans le bas de la page) :
Et si le HTML brut n'est pas autorisé?
Si la seule manière de contribuer est d'utiliser Markdown, Rest, or d'autres syntaxes de type wiki, il est impossible d'entrer directement du mauvais HTML, en théorie.
En fait, pour Markdown ça dépend de la marque de votre markdown (flavor), parce qu'en théorie vous pouvez toujours entrer du HTML brut dans du markdown, sauf si cette fonctionnalité est retirée.
Dans tous les cas, avec un langage qui génère du HTML le jeu est de trouver une syntaxe étrange qui va générer ce mauvais HTML.
Attention, les modes preview sont le plus souvent générés à partir de librairies et de parseurs js, ces parseurs sont habituellement moins robustes que le vrai générateur final. Donc une mauvais syntaxe en mode preview ne signifie à peu près rien.
Je ne vous donnerai pas d'erreurs de syntaxe spécifiques, cela dépend du moteur, vous devrez chercher par vous-même.
Protections
Si vous voulez autoriser les contributions voici quelques points de réflexion :
- Pourquoi ne pas autoriser uniquement le text non enrichi, sans formatage ?
- Pourquoi autoriser
<div>
, si vous utilisez<div>
dans votre mise en page vous pourriez en fait empêcher l'usage de cette balise HTML dans les contributions - Plus globalement, n'autorisez pas les balises que vous utilisez dans votre structure de mise en page (comme
article
ousection
) - Vérifiez dans votre CMS, il existe sans doute des fonctions qui appliquent un nettoyage du HTML (il y en a une dans Drupal par exemple)
- Évitez les regexes pour nettoyer (filtrer) le HTML contribué, préférez les outils basés sur une analyse DOM quand ils sont disponibles, ces outils vont analyser le flux de texte, fabriquer un arbre DOM, puis re-générer une sortie HTML à partir de l'arbre, donc les balises seront dans le bon ordre, et peuvent même être filtrées sur le nettoyage d'attributs et d'identifiants.
- Cela dit, les filtres se basant sur des regexes peuvent aussi marcher et effectuer cette même tâche de filtrage, voici par exemple un très bon module drupal 7, wysiwyg_filter
Actualités en lien
Nouvelle Journée Technique du PRNSN : le numérique dans les pratiques sportives de nature
Le 27 novembre 2024, Montpellier accueille la 18e Journée technique du réseau national des sports de nature, organisée par le PRNSN.
Mini-guide à l’usage des collectivités : l’Open Data, entre nécessité et opportunité
Tout ce que vous avez toujours voulu savoir sur l’Open Data. Petit guide à destination des collectivités pour l’appréhender et se l’approprier.
Makina Corpus, sponsor et conférencier au Capitole du Libre 2024
Les 16 et 17 novembre à Toulouse, Makina Corpus s’engage aux côtés de la communauté du logiciel libre en sponsorisant le Capitole du Libre. Ne manquez pas la conférence de notre expert Drupal, Simon George.