¿Cómo uso Markdown de forma segura?

40

¿Cómo uso la biblioteca Markdown de forma segura? ¿Qué debo hacer para asegurarme de que su salida sea segura para incluirla en mi página web?

Quiero permitir que usuarios no confiables ingresen contenido (en formato Markdown). Usaré el procesador Markdown para generar HTML, y me gustaría incluir ese HTML en mi página web. ¿Qué debo hacer para asegurarme de que esto sea seguro y no es una vulnerabilidad XSS autoinfligida? ¿Qué argumentos necesito pasar? ¿Hay algún preprocesamiento o postproceso que deba hacer? Estoy usando la biblioteca python-markdown, si eso es relevante.

    
pregunta D.W. 06.05.2012 - 05:51
fuente

2 respuestas

14

Uso recomendado. La respuesta corta es: Use markdown(untrusted, safe_mode=remove, enable_attributes=False) .

Asegúrese de tener una versión actualizada de la biblioteca Markdown, ya que las versiones anteriores tienen algunos problemas de seguridad.

También puede ejecutar la salida a través de un desinfectante HTML, como HTML Purifier.

Justificación. Es una buena idea desactivar enable_attributes . Mientras que las últimas versiones de desarrollo de la biblioteca de reducción de Python inhabilitar enable_attributes por defecto si establece safe_mode / a > Las versiones anteriores no lo hicieron. En consecuencia, el solo hecho de establecer safe_mode es no es suficiente en la mayoría de las versiones de la biblioteca Markdown . Si acaba de establecer safe_mode , el resultado es inseguro:

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

Por el momento, las correcciones solo están presentes en git. En el momento de escribir este artículo, la última versión publicada de Python Markdown (2.1.1) sigue siendo vulnerable a menos que establezca explícitamente enable_attributes=False . Por lo tanto, es plausible que muchos sistemas que actualmente usan Python Markdown sean vulnerables.

La documentación podría ser mejor para advertir a los usuarios de Markdown sobre estas trampas. Dice cosas como "Es posible que también desee establecer enable_attributes=False al usar safe_mode ", sin revelar que no hacerlo crea un agujero XSS con todas las versiones más recientes de la biblioteca. Las versiones posteriores de la documentación dicen que la configuración de enable_attributes "podría permitir que un usuario no confiable inyecte JavaScript en sus documentos"; sería más claro decir que la configuración enable_attributes permite a los usuarios inyectar Javascript en sus documentos y, por lo tanto, es muy inseguro si el Markdown puede provenir de una fuente no confiable.

Dudas. Dicho esto, no estoy 100% seguro de si el resultado será seguro, incluso cuando se usa como se recomienda anteriormente. Los desarrolladores han hecho comentarios como los siguientes:

  

"modo seguro" fue una mala elección de nombre que continuamos usando para la comparación con versiones anteriores (el código antiguo aún funciona con nuestras versiones más nuevas). Lo que realmente es es un modo sin marcas. En otras palabras, es solo una manera de no permitir el html en bruto y realmente no garantiza la seguridad.

Ese tipo de comentarios son un poco de miedo.

En las versiones anteriores de la biblioteca de Markth de Python, su saneamiento de HTML me parece un poco frágil, por lo que no estoy seguro de si confiaría en las versiones anteriores de la biblioteca de Markdown, independientemente de las banderas que se hayan pasado. Considera lo siguiente:

>>> 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>'

Me parece una decisión de diseño bastante dudosa: permitir las URL de estilo javascript: a través del procesamiento de Markdown. Se siente como si estuviera dentro de un salto, un salto y un salto de XSS. Todo lo que falta es una manera de romper con el comentario de estilo C ++ (el // ), y se acabó el juego. Por ejemplo:

>>> 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>'

¿Qué tan seguro debo estar de que ningún navegador ejecutará ese Javascript? No lo sé, pero no me está dando sentimientos cálidos y confusos. Si es seguro, es solo suerte ciega.

Afortunadamente, la última versión publicada de Markdown parece hacer un filtrado más estricto de las secuencias de comandos si establece enable_attributes=False . Pero asegúrese de configurar enable_attributes=False , de lo contrario Markdown recurre al frágil desinfección HTML que se encuentra en las versiones anteriores, y no confío en la seguridad de ese esquema.

Qué no hacer. Lo siguiente no es seguro: markdown(escape(untrusted)) .

  • Es posible que piense que, al escapar primero de la entrada, se eliminaría todo el HTML y se haría este uso seguro. De hecho, he visto esto usado en algunos sistemas y recomendado por algunos. Sin embargo, en realidad no es seguro, ya que escapar no es suficiente para hacer que las URL sean seguras. Por ejemplo, este uso de Markdown puede ser anulado por " [clickme](javascript:alert%28%22xss%22%29) ". En general, escapar de la entrada a Markdown es no es el enfoque correcto ; El enfoque correcto es invocar Markdown de la manera adecuada (y posiblemente aplicar un filtro HTML a su salida, si desea protección adicional).

Si usas Django. Si usas Django, la siguiente debería ser una forma segura de usar Markdown:

{{ untrusted | markdown:"safe" }}

A partir de Django 1.4 , esto es seguro. cuando pasa el argumento "safe" , Django ahora tiene soporte especial para configurar safe_mode y deshabilitar enable_attributes . Pero asegúrese de actualizar a Django 1.4 o posterior; en versiones anteriores, este uso era inseguro .

    
respondido por el D.W. 06.05.2012 - 22:46
fuente
13

Markdown solo no sería suficiente para optimizar la salida, ya que permite entradas HTML / Javascript arbitrarias y simplemente las pasa sin procesar.

Por ejemplo, Esta es una rebaja válida:

## heading

text

Pero también esto:

## heading

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

De la página de sintaxis de reducción de marca :

  

Para cualquier marca que no esté cubierta por la sintaxis de Markdown, simplemente use el propio HTML. No es necesario prologarlo o delimitarlo para indicar que está cambiando de Markdown a HTML; solo usas las etiquetas.

Acabo de hacer una prueba rápida con python-markdown y parece que funciona de esta manera.

Dicho esto, dado el conjunto de caracteres limitado que utiliza la sintaxis de reducción de marca, podría ser más fácil filtrar el conjunto de caracteres que permite a los usuarios proporcionar antes que lo alimente a markdown (por ejemplo, algo como a-zA-Z* #+:/&?=-_()> ), pero incluso eso podría ser suficiente para confundir algún código que lo analiza / codifica ... Así que no estoy realmente seguro de cuánta seguridad se obtiene simplemente por el hecho de que usa el ajuste de marca.

ACTUALIZACIÓN:

tras una investigación adicional, encontré esta respuesta en SO que parece bastante razonable.

Luego también busqué más y descubrí el cambio safe_mode ( mencionado aquí y here ).

Una prueba rápida parece funcionar bastante bien, pero podría merecer más investigación ...

>>> 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>'

Conjunto de opciones completo para safe_mode disponible en la página de documentación , que también menciona haber establecido enable_attributes establecido a False por seguridad.

    
respondido por el Yoav Aner 06.05.2012 - 12:00
fuente

Lea otras preguntas en las etiquetas