web-dev-qa-db-fra.com

Comment utiliser Markdown en toute sécurité?

Comment utiliser la bibliothèque Markdown en toute sécurité? Que dois-je faire pour m'assurer que sa sortie est sûre à inclure dans ma page Web?

Je souhaite autoriser les utilisateurs non fiables à saisir du contenu (au format Markdown). J'utiliserai le processeur Markdown pour générer du HTML, et j'aimerais inclure ce HTML dans ma page Web. Que dois-je faire pour m'assurer que cela est sécurisé et qu'il ne s'agit pas d'une vulnérabilité XSS auto-infligée? Quels arguments dois-je transmettre? Y a-t-il un prétraitement ou un post-traitement que je dois faire? J'utilise la bibliothèque python-markdown, si cela est pertinent.

45
D.W.

Utilisation recommandée. La réponse courte est: Utilisez markdown(untrusted, safe_mode=remove, enable_attributes=False).

Assurez-vous d'avoir une version à jour de la bibliothèque Markdown, car les anciennes versions ont des problèmes de sécurité.

Vous pouvez également exécuter la sortie via un filtre HTML, comme HTML Purifier.

Justification. C'est une bonne idée de désactiver enable_attributes. Alors que les dernières versions de développement de la bibliothèque de démarques Python vont désactiver enable_attributes Par défaut si vous définissez safe_mode , les versions antérieures ne le faisaient pas. Par conséquent, le simple réglage de safe_mode Est pas suffisant sur la plupart des versions de la bibliothèque Markdown . Si vous venez de définir safe_mode, Le résultat n'est pas sûr:

import markdown
>>> markdown.markdown("{@onclick=alert('hi')}some paragraph", safe_mode=True)
u'<p onclick="alert(\'hi\')">some paragraph</p>'

Pour le moment, les correctifs ne sont présents que dans git. Au moment d'écrire ces lignes, la dernière version publiée de Python Markdown (2.1.1) reste vulnérable sauf si vous définissez explicitement enable_attributes=False. Par conséquent, il est plausible que de nombreux systèmes utilisant actuellement Python Markdown soient vulnérables.

La documentation pourrait être meilleure pour avertir les utilisateurs de Markdown de ces pièges. Il dit des choses comme "Vous pouvez également définir enable_attributes=False Lorsque vous utilisez safe_mode", Sans divulguer que si vous ne le faites pas, cela crée un trou XSS avec toutes les versions de la bibliothèque, à l'exception des toutes dernières. Les versions ultérieures de la documentation indiquent que la définition de enable_attributes "Pourrait potentiellement permettre à un utilisateur non fiable d'injecter JavaScript dans vos documents"; il serait plus clair de dire que le réglage de enable_attributes permet aux utilisateurs d'injecter Javascript dans vos documents et est donc très peu sûr si le Markdown peut provenir d'une source non fiable.

Doutes. Cela dit, je ne suis pas sûr à 100% si le résultat sera sûr, même lorsque vous l'utilisez comme recommandé ci-dessus. Les développeurs ont fait des commentaires comme celui-ci:

"mode sans échec" était un mauvais choix de nom que nous continuons à utiliser pour la comparabilité en amont (l'ancien code fonctionne toujours avec nos versions plus récentes). Ce que c'est vraiment, c'est un mode sans balisage. En d'autres termes, c'est juste un moyen de refuser le HTML brut et ne garantit vraiment pas la sécurité.

Ce genre de commentaires est un peu effrayant.

Dans les versions antérieures de la bibliothèque Markdown Python, sa désinfection HTML me semble un peu fragile, donc je ne sais pas si je ferais confiance aux versions antérieures de la bibliothèque Markdown, quels que soient les drapeaux passés. Considérer ce qui suit:

>>> markdown.markdown("[Example](javascript://alert%28%22xss%22%29)", safe_mode=True)
u'<p><a href="javascript://alert%28%22xss%22%29">Example</a></p>'

Autoriser des URL de style javascript: À travers le traitement de Markdown me semble être une décision de conception assez douteuse. On dirait que c'est dans un saut, un saut et un saut de XSS. Tout ce qui manque est un moyen de sortir du commentaire de style C++ (le //), Et c'est fini. Par exemple:

>>> markdown.markdown("[Example](javascript://\nalert%28%22xss%22%29)", safe_mode=True)
u'<p><a href="javascript://&#10;alert%28%22xss%22%29">Example</a></p>'

Dans quelle mesure devrais-je être sûr qu'aucun navigateur n'exécutera ce Javascript? Je ne sais pas, mais ça ne me donne pas de sentiments chauds et flous. S'il est sécurisé, ce n'est qu'une chance aveugle.

Heureusement, la toute dernière version publiée de Markdown semble effectuer un filtrage plus strict des scripts si vous définissez enable_attributes=False. Mais assurez-vous de définir enable_attributes=False, Sinon Markdown revient à la fragilisation du nettoyage HTML trouvée dans les versions précédentes, et je ne suis pas confiant dans la sécurité de ce schéma.

Ce qu'il ne faut pas faire. Ce qui suit n'est pas sécurisé: markdown(escape(untrusted)).

  • Vous pourriez penser que le premier échappement de l'entrée supprimerait tout le code HTML et sécuriserait cette utilisation. En fait, j'ai vu cela utilisé dans certains systèmes et recommandé par certains. Cependant, il n'est en fait pas sûr, car l'échappement ne suffit pas pour sécuriser les URL. Par exemple, cette utilisation de Markdown peut être annulée par "[clickme](javascript:alert%28%22xss%22%29)". En général, échapper à l'entrée de Markdown est pas la bonne approche ; la bonne approche consiste à invoquer Markdown de la manière appropriée (et éventuellement à appliquer un filtre HTML à sa sortie également, si vous souhaitez une protection supplémentaire).

Si vous utilisez Django. Si vous utilisez Django, ce qui suit devrait être un moyen sûr d'utiliser Markdown:

{{ untrusted | markdown:"safe" }}

Depuis Django 1.4 , c'est sûr. lorsque vous passez l'argument "safe", Django dispose désormais d'un support spécial pour définir safe_mode et désactiver enable_attributes. Mais assurez-vous de mettre à jour vers Django 1.4 ou version ultérieure; dans les versions antérieures, cette utilisation n'était pas sécurisée .

16
D.W.

Le marquage à lui seul ne serait pas suffisant pour santiser la sortie, car il permet une entrée HTML/Javascript arbitraire et la transmet simplement non traitée.

Par exemple. c'est une démarque valide:

## heading

text

Mais aussi ceci:

## heading

text <script>alert('hello');</script>

Depuis la page de syntaxe de démarque :

Pour tout balisage qui n'est pas couvert par la syntaxe de Markdown, vous utilisez simplement HTML lui-même. Il n'est pas nécessaire de le faire précéder ou de le délimiter pour indiquer que vous passez de Markdown à HTML; vous utilisez simplement les balises.

Je viens de faire un test rapide en utilisant python-markdown et cela semble fonctionner de cette façon.

Cela dit, étant donné le jeu de caractères limité utilisé par la syntaxe de démarquage, il pourrait être plus facile de filtrer le jeu de caractères que vous autorisez les utilisateurs à fournir avant de le nourrir. pour démarquer (par exemple quelque chose comme a-zA-Z* #+:/&?=-_()>), mais même ceux-ci pourraient suffire à confondre du code qui le paralyse/l'encode ... Donc, je ne suis pas vraiment sûr de la sécurité que vous obtenez purement du fait que vous utilisez le démarque.

MISE À JOUR:

suite à de nouvelles recherches, je a trouvé cette réponse sur SO ce qui semble tout à fait sensé.

J'ai ensuite cherché plus loin et découvert le commutateur safe_mode ( mentionné ici et ici ).

Un test rapide semble assez bien fonctionner, mais il pourrait mériter des recherches supplémentaires ...

>>> import markdown
>>> markdown.markdown("<script>alert('hello');</script> hello <strong>world</strong>")
u"<script>alert('hello');</script>\n\n<p>hello <strong>world</strong></p>"
>>> markdown.markdown("<script>alert('hello');</script> hello <strong>world</strong>", safe_mode=True)
u'<p>[HTML_REMOVED]</p>\n<p>hello [HTML_REMOVED]world[HTML_REMOVED]</p>'

Ensemble d'options complet pour safe_mode disponible sur la page de documentation - qui mentionne également que enable_attributes Est réglé sur False pour des raisons de sécurité.

13
Yoav Aner