Cómo proteger aplicaciones Flask de Python
Gourav Singh Bais
21 de mayo de 2024
0 minutos de lecturaFlask es un framework web potente, ligero y versátil para Python, diseñado para que los desarrolladores puedan crear aplicaciones web rápidamente y con un mínimo de código repetitivo. Es un microframework independiente que no necesita bibliotecas ni herramientas adicionales y no cuenta con una capa de abstracción de bases de datos.
Flask incluye funciones como enrutamiento, renderizado de plantillas y manejo de solicitudes, y sigue el estándar Web Server Gateway Interface (WSGI), conocido por su simplicidad y flexibilidad.
Por desgracia, como cualquier otro framework web, Flask es vulnerable si no se protege adecuadamente. Los riesgos de seguridad más comunes para Flask incluyen el cross-site scripting (XSS), la falsificación de solicitudes entre sitios (CSRF) y la inyección SQL.
En Snyk Vulnerability Database puedes ver algunas de las muchas vulnerabilidades de seguridad encontradas en la biblioteca Flask de Python:

En este artículo, conocerás algunas prácticas recomendadas para proteger aplicaciones de Python creadas con el framework web Flask. Primero, revisarás algunos ejemplos de configuraciones inseguras y luego aprenderás a mitigar y corregir los problemas.
¿Qué es el estándar de interfaz de puerta de enlace del servidor web (WSGI)?
WSGI ofrece una forma estandarizada para que los servidores web se comuniquen con aplicaciones web escritas en Python. Es una capa intermediaria que permite a los servidores web enviar solicitudes a una aplicación o framework web y, luego, entregar las respuestas al cliente.
¿Por qué es importante WSGI?
Antes de WSGI, los frameworks y servidores web de Python solían estar estrechamente vinculados; esto significaba que un framework específico solo podía ejecutarse en determinados servidores web. WSGI eliminó esta limitación y permitió una mayor flexibilidad e interoperabilidad.
¿En qué se parece WSGI a conceptos similares de otros lenguajes?
WSGI tiene un propósito similar al de la API Java Servlet en Java o Rack en Ruby. Estas tecnologías abstraen los detalles de las solicitudes y respuestas HTTP, lo que permite a los desarrolladores enfocarse en escribir la lógica de las aplicaciones web en lugar de lidiar con la comunicación subyacente del servidor.
Configuraciones inseguras en aplicaciones Flask de Python
A continuación, se muestran algunos de los problemas de configuración insegura más comunes que afectan a las aplicaciones Flask de Python y que los desarrolladores deben tener muy presentes para evitar repetir estos errores en sus bases de código:
Exposición de claves secretas
La divulgación accidental de una clave secreta es un fallo de seguridad común que implica información confidencial, como la clave secreta de Flask, las claves de API y las contraseñas, dentro del código fuente. Integrar datos confidenciales directamente en el código fuente representa un riesgo de seguridad considerable, ya que actores maliciosos pueden aprovechar esta vulnerabilidad para obtener acceso no autorizado a varios servicios.
Si codificaste por error la clave secreta y la clave de API en el código fuente, se verá así:
Consejo: este es un buen momento para instalar la extensión de Snyk en tu IDE y recibir una indicación visual de la exposición de claves secretas, como la línea azul ondulada que puedes ver en mi editor:

En este caso, para mitigar la exposición de claves secretas, debes generar la clave secreta de forma segura. Aunque hay varias maneras de hacerlo, una de las más comunes es usar el módulo uuid. El módulo uuid se usa principalmente para generar identificadores únicos universales (UUID) según los principios definidos en RFC 4122. Este módulo genera una cadena hexadecimal aleatoria de 32 bytes que se puede usar como clave secreta.
Para generar una clave segura, importa el módulo uuid en tu código y usa la función uuid4().hex:
Sin embargo, no basta con generar la clave secreta. También debes usarla de forma segura en tu aplicación Flask. La forma segura recomendada es asignar la clave secreta a una environment variable o usar un archivo de configuración cifrado y guardar la clave allí. Si decides asignar la clave secreta a una environment variable, se verá así:
Los desarrolladores deben seguir convenciones de configuración como las recomendadas en la aplicación de 12 factores, que aconsejan usar variables de entorno para almacenar las credenciales y así evitar filtraciones de datos, accesos no autorizados y posibles usos indebidos.
Modo de depuración activado
El modo de depuración suele estar pensado para entornos de desarrollo y proporciona mensajes de error detallados e información completa sobre el estado de la aplicación. Sin embargo, activarlo en un entorno de producción expone información confidencial y el funcionamiento interno del sistema.
Por ejemplo, en esta aplicación web Flask, el modo de depuración está activado en producción:
En este caso, el modo de depuración está configurado como True, lo que significa que, si ocurre un error en producción, la consola de depuración puede exponer información confidencial, incluidos nombres de usuario y contraseñas.
En esta situación, es muy fácil olvidar que configuraste el modo de depuración como True y no cambiarlo antes de implementar la aplicación en producción. Por eso conviene usar una plataforma como Snyk, que puede ayudarte a encontrar y corregir vulnerabilidades en tu código y tus aplicaciones. Snyk admite una amplia variedad de lenguajes de programación, como Python, Go, PHP, JavaScript y otros.
Además, puedes integrar Snyk fácilmente con varios IDE, como Visual Studio Code y PyCharm, así como con pipelines de CI, como Jenkins, CircleCI y Maven, y con flujos de trabajo.
Por ejemplo, para usar Snyk en este caso de depuración, solo tienes que buscar "Snyk" en Visual Studio Code e instalarlo:

Una vez instalado, Snyk aparecerá en el panel de la izquierda. Haz clic en el botón Connect para iniciar sesión o crear una cuenta de Snyk si aún no tienes una. Luego aparecerá una ventana del navegador como esta:

Una vez conectado, vuelve a Visual Studio Code y abre la carpeta donde está guardado tu script de Flask. Ahora deberías poder ver las vulnerabilidades de tu código:

Como puedes ver, Snyk detectó que el modo de depuración seguía activado. Para mitigar este problema, debes proteger tu aplicación Flask configurando el modo de depuración como False:
Además de desactivar el modo de depuración en producción, también debes usar el mecanismo de registro para recopilar los registros de la aplicación y revisar si hay posibles problemas en el código.
Datos confidenciales sin protección en archivos de configuración
Guardar información crítica (p. ej., credenciales de bases de datos, claves de API y claves de cifrado) en archivos de configuración es una práctica común en las aplicaciones Flask. Sin embargo, los datos pueden quedar expuestos si no proteges estos archivos.
Considera el siguiente caso, en el que tu archivo de configuración (generalmente config.ini) contiene las credenciales para conectarse a la base de datos:
En este caso, el nombre de usuario y la contraseña están en texto sin formato. Por lo tanto, si un atacante obtiene acceso a este archivo, puede usar las credenciales para acceder a información confidencial.
La mejor manera de resolver este problema es evitar los archivos de configuración y usar variables de entorno para guardar tus credenciales. Como las variables de entorno almacenan toda la información necesaria en un archivo oculto, como .bash_profile, puedes simplemente usar gitignore en este archivo para evitar subirlo por error a plataformas de control de versiones, como Git o Bitbucket, a las que los atacantes pueden acceder fácilmente.
Si necesitas usar archivos de configuración en tu aplicación Flask, asegúrate de que los datos confidenciales estén cifrados:
En este caso, se usa un marcador de posición o un valor cifrado para representar la contraseña. En este escenario, se puede acceder a los datos confidenciales durante la ejecución mediante un sistema externo y seguro de administración de claves o una técnica de descifrado segura.
Vulnerabilidades en aplicaciones Flask
Ahora que conoces un poco mejor algunas configuraciones inseguras en aplicaciones Flask de Python, es momento de revisar algunas vulnerabilidades de seguridad web comunes que también pueden afectar a las aplicaciones Flask.
Cross-site scripting (XSS)
El XSS es una vulnerabilidad de seguridad web en la que un atacante intenta insertar código JavaScript o HTML arbitrario en una página web. Esto permite que los atacantes ejecuten scripts maliciosos en el navegador de un usuario.
Jinja2, el motor de plantillas que usa Flask, ofrece la función Auto Escape para manejar vulnerabilidades XSS. De forma predeterminada, todas las variables se escapan automáticamente (es decir, no contienen ningún elemento HTML, salvo que se indique lo contrario). Esto significa que Jinja2 escapa de forma segura cualquier entrada del usuario que contenga código HTML o JavaScript, lo que impide la ejecución de scripts maliciosos.
Si el escape automático está configurado como False, la aplicación Flask es vulnerable a ataques XSS. Por ejemplo, aquí tienes una aplicación web que necesita una entrada del usuario para renderizar una página:
La página index.html correspondiente, donde el escape automático está configurado como false, se vería así:
Si un atacante inserta el script malicioso mediante el parámetro input, este se ejecuta cuando se renderiza la página, lo que puede provocar riesgos de seguridad como el robo de datos, la toma de control de cuentas y ataques de phishing.
Para mitigar el XSS, los desarrolladores deben configurar el sitio web para que escape correctamente el texto y no incluya ningún elemento HTML. Solo tienes que configurar el escape automático como true para proteger la aplicación Flask de un ataque XSS:
Otra forma de prevenir el XSS es escapar explícitamente el marcado. Para hacerlo, debes usar el filtro |safe en una página web, así:
Sin embargo, debes usar |safe con mucho cuidado, ya que supone que el contenido marcado como seguro no contiene ningún script malicioso.
Para obtener más información sobre las vulnerabilidades de cross-site scripting en Python, visita una breve lección de seguridad en Snyk Learn.
Falsificación de solicitudes entre sitios (CSRF)
El CSRF es un ataque malicioso que engaña a la víctima para que envíe una solicitud maliciosa. En una aplicación web, el CSRF ocurre cuando un atacante realiza una solicitud no autorizada a una página web en nombre de un usuario autorizado. Este tipo de ataque suele verse en plataformas como Facebook: alguien envía una imagen a un grupo y, si los usuarios del grupo hacen clic en ella, se elimina su cuenta.
Para entender mejor el CSRF, considera una aplicación sencilla que cambia el estado y funciona sin protección contra CSRF, como esta:
Este código envía un formulario para cambiar la contraseña mediante una solicitud POST. Como no hay protección contra CSRF, un atacante puede engañar a los usuarios para que cambien su contraseña dirigiéndolos a otro sitio web que contiene HTML o un script diseñado para ese fin. Un formulario sin protección contra CSRF se ve así:
Para mitigar las vulnerabilidades CSRF, debes usar la extensión Flask-WTF junto con el middleware CSRFProtect. Flask-WTF es un complemento de Flask que incorpora la biblioteca WTForms, que ofrece funciones prácticas para generar y administrar fácilmente formularios para una aplicación Flask.
Puedes instalarlo con pip de la siguiente manera:
Una vez instalado, puedes actualizar tu aplicación Flask con Flask-WTF para protegerla contra CSRF:
En este caso, CSRFProtect se inicializa con la aplicación Flask y se configura SECRET_KEY. Esta clave se usa específicamente para crear y validar tokens CSRF.
No olvides actualizar la plantilla (index.html) para incluir el token CSRF, así:
La función csrf_token() genera un campo de entrada oculto que contiene el token CSRF. Cuando se envía el formulario, Flask-WTF valida el token para asegurarse de que la solicitud no sea un ataque CSRF.
Inyección FSQL y seguridad de bases de datos
Ahora, revisemos algunos problemas de Flask relacionados con la seguridad de las bases de datos:
Inyección SQL
La inyección SQL es un problema grave en el que un atacante ejecuta código SQL arbitrario en una base de datos al manipular los parámetros de una consulta SQL. Esto puede provocar accesos no autorizados, manipulación de datos y divulgación de información confidencial.
En las aplicaciones Flask, puede producirse un ataque de inyección SQL si las entradas no se depuran correctamente antes de usarse en una consulta SQL.
Para entender mejor este ataque, considera un ejemplo de código Flask que selecciona datos de una base de datos, así:
Esta consulta espera el nombre de usuario y la contraseña en la cadena de consulta. Si un atacante inserta entradas como ' OR '1'='1'; -- , la consulta queda así:
Como esta consulta siempre devuelve True, el atacante iniciaría sesión y obtendría acceso no autorizado a los datos.
Identificar código vulnerable a la inyección SQL es difícil, pero por suerte Snyk puede ayudarte. Como ya configuraste Snyk en Visual Studio Code, solo tienes que abrir el script y Snyk detectará automáticamente el problema de inyección SQL:

Una forma sencilla de mitigar este ataque de inyección SQL es usar consultas o instrucciones parametrizadas. En
En Flask, también puedes usar el método execute del objeto cursor para vincular parámetros de forma segura. Una consulta parametrizada se ve así:
En este código se usan marcadores de posición ?, cuyos valores reales se proporcionan como una tupla en el método execute. Así, las entradas de los usuarios se tratan como datos y no como código SQL ejecutable.
Asegúrate de validar y depurar siempre las entradas de los usuarios para evitar este tipo de ataque. Puedes depurar las entradas con técnicas como caracteres de escape, bibliotecas de terceros y expresiones regulares.
Para obtener información más detallada sobre las inyecciones SQL, te recomiendo leer Guía rápida sobre inyección SQL: 8 prácticas recomendadas para prevenir ataques de inyección SQL.
Autenticación y autorización
La autenticación y la autorización son componentes fundamentales de la seguridad web. Una brecha en la autenticación y la autorización puede provocar varios problemas de seguridad, que veremos a continuación.
Problemas de seguridad de autenticación
Uno de los problemas de seguridad de autenticación más comunes es el ataque de fuerza bruta, en el que un atacante intenta repetidamente adivinar un nombre de usuario y una contraseña para acceder a un sitio web o servicio. Otro problema común es la fijación de sesión, en la que el atacante establece el ID de sesión en un valor conocido, posiblemente mediante un ataque de phishing.
Para mitigar los ataques de fuerza bruta, debes implementar un mecanismo de bloqueo de sesión. Con este enfoque, se bloquea al usuario después de cierto número de intentos fallidos de inicio de sesión:
En este código, defines MAX_LOGIN_ATTEMPTS como 3, lo que significa que si alguien intenta ingresar credenciales incorrectas más de tres veces, se le bloqueará.
En el método login(), obtienes el username y la password, y verificas si el usuario está bloqueado. Si no lo está, revisas los intentos de inicio de sesión. Si las credenciales son correctas, todo está listo. De lo contrario, el número de intentos aumenta en uno y el proceso se repite hasta que el usuario inicia sesión correctamente o queda bloqueado.
Además de implementar un mecanismo de bloqueo de sesión, también debes usar políticas de contraseñas seguras y considerar la autenticación multifactor (MFA).
Para mitigar los problemas de fijación de sesión, puedes regenerar los ID de sesión después de que un usuario inicie sesión o después de cierto tiempo (como vencimiento automático), aunque el usuario siga con la sesión iniciada:
En este código, el método login() regenera el ID de sesión con el método regenerate() cuando el usuario inicia sesión correctamente.
Problemas de seguridad de autorización
Al desarrollar una aplicación web, no todas las funciones están pensadas para todos los usuarios (es decir, un usuario específico solo debería tener acceso a un conjunto determinado de funciones). Si los roles no se asignan correctamente a los usuarios, puede producirse un acceso no autorizado; por eso necesitas un control de acceso basado en roles (RBAC).
También es posible que los usuarios autenticados accedan a recursos no autorizados al manipular parámetros o URL, lo que suele llamarse referencias directas a objetos inseguras (IDOR). Te recomiendo leer esta publicación sobre cómo detectar y corregir referencias directas a objetos inseguras en Python para obtener más información.
Para resolver los problemas de RBAC e IDOR, debes asignar roles específicos a los usuarios y aplicar controles de acceso según esos roles, así:
En este código, el método admin_dashboard() implementa las funciones a las que se debería acceder si el rol de acceso es admin. De lo contrario, devuelve una advertencia de Unauthorized access.
También debes administrar el control de acceso para que los usuarios solo puedan acceder a los recursos para los que tienen autorización:
No debes almacenar credenciales en texto sin formato, porque los atacantes podrían acceder a ellas fácilmente con fines maliciosos. Para evitarlo, debes usar hashing y salt para almacenar las credenciales de los usuarios.
El hashing transforma una contraseña en una cadena de caracteres de longitud fija, por lo que resulta computacionalmente inviable revertir el proceso y obtener la contraseña original. El salting consiste en agregar un valor aleatorio único (el salt) a cada contraseña antes de aplicar el hashing, lo que ayuda a prevenir ataques como los de tablas rainbow.
Bibliotecas desactualizadas y actualizaciones de seguridad
Si usas bibliotecas de Flask desactualizadas, pueden contener vulnerabilidades que los atacantes podrían aprovechar. Las bibliotecas suelen publicar actualizaciones y parches de seguridad para corregir vulnerabilidades. Asegúrate de usar siempre la versión más reciente del paquete.
Para asegurarte de usar los parches de seguridad más recientes, puedes usar herramientas como pip o conda. Además, puedes usar herramientas como Snyk para analizar todo tu código en busca de dependencias y configuraciones de infraestructura como código (IaC).
Otra práctica recomendada es fijar las dependencias en el archivo requirements.txt. Así, el proyecto usa versiones específicas, lo que permite a los desarrolladores controlar las actualizaciones y probar la compatibilidad.
Carga segura de archivos
Si un sitio web de Flask permite que cualquier persona cargue cualquier tipo de archivo, pueden surgir varios problemas. Por ejemplo, si un atacante carga archivos maliciosos en el servidor, puede provocar un ataque de denegación de servicio (DoS) y posiblemente sobrescribir archivos existentes. Algunos archivos ejecutables también pueden producir distintos tipos de virus informáticos.
A continuación, se muestra un ejemplo de código que permite a cualquier usuario cargar archivos de cualquier tipo:
En este código, el método upload_file() lee el archivo que proporciona el usuario como entrada y lo guarda en la carpeta uploads.
La primera solución para evitar archivos inseguros es definir un conjunto de extensiones de archivo permitidas. Nunca debes permitir tipos de archivos ejecutables o de script en tu aplicación Flask.
Además, asegúrate de guardar los archivos en una ubicación segura para evitar el acceso no autorizado:
Aquí se aplican varias condiciones para comprobar que los tipos de archivo sean auténticos.
Otra práctica recomendada es asignar nombres aleatorios a los archivos para evitar que se sobrescriban y reducir el riesgo de usar convenciones de nombres predecibles:
Manejo de errores y divulgación de información
El manejo de errores es importante en cualquier tipo de aplicación. En producción, no es recomendable usar instrucciones print ni habilitar el modo de depuración. Si usas un mecanismo de registro inadecuado, podrías revelar varios detalles importantes sobre tu aplicación.
Por ejemplo, mostrar mensajes de error detallados en producción puede revelar información confidencial sobre el funcionamiento interno de la aplicación, lo que facilita que los atacantes identifiquen posibles vulnerabilidades:
Este ejemplo revela la información más importante sobre la base de datos asociada con la aplicación Flask.
Además, mostrar los seguimientos completos de errores en las respuestas puede revelar la estructura del código de la aplicación y posiblemente exponer vulnerabilidades como esta:
Para mitigar la divulgación de información mediante el manejo de errores, puedes crear páginas de error personalizadas para códigos de error genéricos (por ejemplo, 404 Not Found y 500 Internal Server Error) sin exponer detalles confidenciales. Para hacerlo, puedes usar el decorador errorhandler para definir controladores de errores personalizados:
Además, debes implementar un registro adecuado para excepciones y errores. Los mensajes de registro deben tener suficiente detalle para depurar, pero no deben exponer información confidencial:
Si sigues estas prácticas recomendadas, podrás garantizar que tu aplicación Flask sea sólida y segura.
Conclusión
Flask es un framework web popular para aplicaciones web de pequeña y mediana escala. Sin embargo, como cualquier otra herramienta o framework de desarrollo web, Flask puede presentar problemas si no se maneja correctamente. Es importante que conozcas estos problemas y también sepas cómo mitigarlos para evitar que actores maliciosos se aprovechen de ellos.
Te invitamos a seguir este repositorio de código en GitHub, donde encontrarás ejemplos completos y funcionales de algunos de los temas de seguridad de aplicaciones Flask que analizamos.
Snyk puede ayudarte a encontrar y mitigar fácilmente cualquier vulnerabilidad en tu código. Snyk es compatible con varios lenguajes de programación y se integra sin problemas con tus herramientas, pipelines y flujos de trabajo. Empieza a usar Snyk gratis hoy mismo.
Por último, asegúrate de seguir la guía rápida de prácticas recomendadas de seguridad en Python de Snyk y adquirir habilidades de seguridad de aplicaciones en Python a través del sitio gratuito Snyk Learn.



