Cómo prevenir XSS en Django
13 de marzo de 2023
0 minutos de lecturaCross-Site Scripting (XSS) es un tipo de vulnerabilidad que consiste en manipular la interacción del usuario con una aplicación web para comprometer el entorno del navegador. Estas vulnerabilidades pueden afectar muchas aplicaciones web, incluidas las creadas con frameworks modernos como Django.
Como los ataques XSS son tan frecuentes, es fundamental proteger tus aplicaciones contra ellos. Esta guía explica cómo se originan las vulnerabilidades XSS en las aplicaciones Django y qué puedes hacer para mitigarlas. También aprenderás a usar herramientas de seguridad gratuitas para detectar y corregir vulnerabilidades XSS en las primeras etapas del desarrollo.
¿Qué es XSS?
El término XSS proviene de los primeros días de estos ataques, cuando el principal objetivo de los atacantes era robar datos de otros sitios. Sin embargo, los ataques XSS han evolucionado y ahora se considera XSS cualquier ataque que permita a los atacantes comprometer datos, tokens u otros elementos del lado del cliente. Los ataques exitosos pueden provocar desde el secuestro de sesiones hasta la toma total de una cuenta o un sistema.
En un ataque XSS, datos no deseados ingresan a una aplicación y se muestran al usuario sin validación. Los datos maliciosos que se envían como parte de la respuesta suelen consistir en JavaScript u otro código que puede ejecutarse en el navegador del usuario. Cuando el navegador ejecuta este código, elude su política del mismo origen (SOP) y explota al usuario.
Históricamente, los ataques XSS se han clasificado en dos tipos según la persistencia de los datos: reflejados y almacenados. También existe un tipo más reciente llamado XSS basado en DOM.
XSS almacenado
Los ataques XSS almacenados, a veces llamados ataques XSS persistentes, ocurren cuando se guarda código malicioso en el servidor de una aplicación vulnerable y luego se envía a los usuarios sin sanitizar (para que finalmente lo ejecute el navegador). La carga maliciosa suele almacenarse en bases de datos, publicaciones de foros, registros o campos de comentarios.

Cuando una víctima visita la aplicación afectada, la carga maliciosa se muestra en su navegador como parte de la respuesta del servidor. Luego, el navegador ejecuta estos datos y el usuario queda comprometido.
XSS reflejado
Los ataques XSS reflejados ocurren cuando la aplicación devuelve datos ingresados por el usuario que el servidor no guarda. En este caso, el código no confiable se envía como parte de un resultado de búsqueda o mensaje de error sin sanitizarlo correctamente.

Por lo general, el atacante envía la carga a la víctima de forma encubierta. Cuando la víctima hace clic en ella, el código malicioso se envía a la aplicación afectada. La aplicación responde con el código malicioso, que se ejecuta en el navegador de la víctima.
XSS basado en DOM
Los ataques XSS basados en DOM ocurren cuando el flujo de datos maliciosos comienza y termina en el DOM del navegador. La vulnerabilidad se activa al manipular el DOM del navegador de la víctima; la respuesta HTTP resultante no cambia, sino que simplemente devuelve el código del lado del cliente, que luego modifica el DOM del navegador de manera que ejecuta la carga.
Cómo prevenir XSS en Django
Django ofrece protecciones integradas contra ataques XSS, como un mecanismo de escape automático para su motor de plantillas. Sin embargo, aunque puede detener los ataques XSS más comunes, no puede prevenir los vectores de ataque que surgen de malas prácticas de programación.
Puedes reducir el riesgo de ataques XSS siguiendo las prácticas recomendadas al crear tu aplicación.
Usa comillas para delimitar los datos dinámicos
Cualquier parte de tu aplicación que acepte entradas del usuario sin sanitizarlas puede introducir vulnerabilidades XSS. Los atacantes pueden usar estos campos de entrada para inyectar código JavaScript arbitrario que no requiere caracteres HTML.
Por ejemplo, el siguiente HTML usa un atributo sin comillas que se puede modificar para incluir manejadores de JavaScript, como onmouseover=alert(1):
Por supuesto, los atacantes reales no serán tan inofensivos. En cambio, inyectarán cargas personalizadas para aprovechar al máximo esta oportunidad. Otro ejemplo de vulnerabilidades XSS derivadas de cargas sin comillas es el uso de variables de JavaScript para almacenar datos del usuario:
Usar números enteros de JavaScript para almacenar un valor proporcionado de esta manera expone tu aplicación a ataques XSS. El mecanismo de escape automático de Django no te protegerá. Un atacante puede ingresar algo tan simple como 23 ; payload() y ejecutar código malicioso en el navegador de un usuario.
Delimitar con comillas dobles todos los atributos visibles para el usuario mitigará estas vulnerabilidades:
Evita los literales de plantilla
Si tu código contiene literales de plantilla de JavaScript, podría ser vulnerable a XSS. Los literales de plantilla usan acentos graves en lugar de comillas y no escapan los caracteres:
Esto permite que los atacantes ejecuten código creando cargas con acentos graves. La mejor forma de evitarlo es prohibir el uso de literales de plantilla.
Valida las URL de los atributos
Muchos atributos HTML, como a href e img src, esperan una URL, lo que puede aprovecharse para realizar ataques XSS. Los atacantes solo tienen que incluir cargas con un URI javascript: para ejecutar su código. El escape de HTML de Django no te protegerá en este caso.
Por ejemplo, imagina que tu aplicación permite a los usuarios agregar la URL de su sitio web personal. Los usuarios maliciosos pueden usar algo como javascript:payload(XSS) como enlace. Así, cada vez que alguien haga clic en él, se ejecutará la carga maliciosa.
Para resolver este problema, puedes crear una lista de protocolos permitidos para las URL y bloquear todos los demás. Algunos URI seguros son http:, https:, ftp: y mailto:. También puedes sanitizar las URL de los atributos antes de guardarlas en la base de datos.
Escapa los datos de JavaScript
Insertar datos variables directamente en JavaScript los coloca en el contexto de ejecución. Si tu programa permite que los usuarios controlen los datos que se insertan en bloques de JavaScript o manejadores de eventos, pueden producirse ataques XSS.
Como los atacantes pueden crear código sin caracteres HTML, confiar en el escape de HTML de Django no evitará XSS en estos casos.
Para mitigar este riesgo, puedes prohibir las variables de plantilla dentro de bloques <script> o usar la etiqueta de plantilla json_script para leer datos de JS:
No olvides codificar los caracteres de los datos con el formato \xHH y verificar que el encabezado Content-Type del JSON sea application/json, no text/html.
Escapa los datos de CSS
Los datos insertados en etiquetas o atributos de estilo CSS pueden introducir vulnerabilidades XSS en tu aplicación Django. Los atacantes pueden aprovechar estos datos CSS para acceder al contexto de ejecución. Por lo general, lo hacen inyectando código JavaScript en contextos CSS:
La directiva expression() permite usar instrucciones JavaScript arbitrarias para evaluar el valor de una propiedad CSS y expone tu aplicación Django a ataques XSS.
Para mitigar este riesgo, evita establecer los valores de los parámetros CSS mediante la directiva expression(). Si usas instrucciones JavaScript para establecer o cambiar propiedades CSS, prueba algo como style.property = x.
Usa safe lo menos posible
El escape automático de Django funciona bien en la mayoría de los casos, pero no puede ayudarte si desactivas el escape de HTML con el filtro safe en una plantilla. Cuando marcas algo como safe, Django no lo escapa y muestra los datos tal cual.
Si tu aplicación muestra esta consulta sin validar los datos, los atacantes podrían aprovecharla para insertar cargas maliciosas. Usa el filtro safe solo cuando tengas la certeza de que los datos son seguros; nunca marques como safe los datos proporcionados por el usuario.
Restringe el uso del filtro safeseq
El filtro safeseq indica que el contenido que se va a mostrar es totalmente seguro. Puede introducir cargas XSS igual que el filtro safe.
Evita usar este filtro con datos de secuencia. Si es necesario, usa mark_safe(). Así podrás revisar las instancias en las que marcaste algo explícitamente como seguro durante la revisión del código.
Limita el uso de html_safe()
El método html_safe() agrega el método mágico __html__ a la clase proporcionada, que devuelve una representación exacta en formato de cadena de esa clase:
El motor de plantillas de Django no escapa estos datos, por lo que representan un posible problema de XSS. No se debe usar html_safe() salvo que sea necesario. También debes evitar usar el método mágico __html__ dentro de cualquier clase.
Evita los filtros con is_safe=True
Si registras un filtro personalizado con is_safe=True, Django no escapará el HTML y marcará como seguro el valor que devuelva el filtro.
Como los datos devueltos por este filtro no pasan por el escape automático de Django, pueden dar lugar a ataques XSS. Para ayudar a mantener la seguridad de tu aplicación, evita registrar filtros de esta manera, a menos que tengas la certeza de que los datos devueltos son seguros.
Limita el uso de mark_safe() y SafeString
El método mark_safe() marca los datos mostrados como seguros y omite las protecciones XSS integradas de Django. Si usas este método con frecuencia, puede convertirse en un caldo de cultivo para vulnerabilidades XSS.
Además, cuando usas mark_safe(), los datos se devuelven como un SafeString. Django usa la clase SafeString para determinar qué datos son seguros para mostrar y cuáles no. Entonces, ¿qué ocurre si usas directamente SafeString?
Como quizá ya imaginabas, usar directamente la clase SafeString de esta manera elude el escape de HTML de Django y puede dar lugar a XSS. Lo mejor es limitar el uso tanto de mark_safe() como de SafeString.
Evita escribir respuestas con HttpResponse
Usar HttpResponse o clases similares directamente en el código elude el sistema de plantillas de Django y, por lo tanto, desactiva el escape de HTML.
Esto puede exponer tu programa a ataques XSS, así que debes evitar escribir respuestas de esta manera. En su lugar, puedes usar el método render() con una plantilla.
Detecta y corrige vulnerabilidades XSS en Django con Snyk
Los ataques XSS son una de las amenazas más activas contra las aplicaciones web modernas. Como pueden ocurrir de muchas maneras, detectar todas las vulnerabilidades XSS durante la revisión del código es difícil. Por eso, es fundamental enfocarse en detectar estas vulnerabilidades en las primeras etapas del ciclo de vida del desarrollo de software (SDLC).
Puedes hacerlo integrando herramientas de prueba especializadas en tu entorno de desarrollo. Para mostrarte cómo, usaremos Snyk.
Snyk ofrece varias maneras de probar aplicaciones Django, como una interfaz web, una interfaz de línea de comandos, complementos para IDE y API. En este tutorial usarás el complemento de Snyk para PyCharm y necesitarás tener instalado PyCharm para seguir los pasos (aunque Snyk también es compatible con otros IDE, como Eclipse y Visual Studio).
Para usar Snyk, crea una cuenta gratuita e instala el complemento siguiendo la guía de instalación.

Después de instalar el complemento, configúralo para tu IDE. Debes conectar el complemento con la plataforma Snyk, lo que requiere la CLI de Snyk. Sin embargo, no necesitas instalarla por separado. Snyk la descargará automáticamente cuando realices un análisis por primera vez.

Se te pedirá que autentiques Snyk. Haz clic en Test code now y accederás a la interfaz web de Snyk.

Haz clic en Authenticate para confirmar la integración del complemento.

Una vez completada la autenticación, Snyk empezará a analizar automáticamente tu aplicación Django y mostrará los problemas en la pestaña Snyk de tu IDE. También puedes iniciar el análisis haciendo clic en Run scan.

Snyk muestra información útil sobre las vulnerabilidades detectadas, como su nivel de gravedad y las correcciones sugeridas.

Puedes corregir las vulnerabilidades siguiendo las actualizaciones recomendadas por Snyk. El complemento siempre recomendará los cambios mínimos necesarios para corregir tu código.
Conclusión
Los ataques XSS son uno de los problemas de seguridad más comunes en las aplicaciones Django. Como pueden surgir de muchas fuentes y adoptar distintas formas, es difícil prevenirlos por completo. Sin embargo, puedes reducir el riesgo de sufrir ataques XSS si sigues estas prácticas recomendadas.
También es fundamental usar las herramientas adecuadas durante el desarrollo. Una plataforma de seguridad especializada como Snyk puede ayudarte a encontrar y corregir vulnerabilidades XSS desde las primeras etapas del desarrollo de tu aplicación.
Comienza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.
