Principales Vulnerabilidades de Seguridad en Python
En los ágiles pipelines de desarrollo actuales, el código Python inseguro puede introducir riesgos graves. Python es apreciado por su simplicidad, pero esa misma flexibilidad puede volverse peligrosa si se descuidan las prácticas de codificación segura. Desde escollos de inyección de código hasta librerías de terceros vulnerables, incluso un pequeño descuido puede abrir la puerta a los atacantes. Estudios recientes muestran millones de credenciales y claves API filtradas accidentalmente en el código, un crudo recordatorio de que la seguridad no puede ser una ocurrencia tardía. En este artículo, desglosaremos algunas de las principales vulnerabilidades de seguridad a nivel de lenguaje Python (más allá de los problemas de los frameworks web), ilustraremos su impacto con ejemplos reales (incluyendo CVEs en librerías populares) y ofreceremos consejos para mitigar cada riesgo. También destacaremos cómo la integración de herramientas de seguridad (como SAST de Aikido, escaneo de secretos y comprobaciones de dependencias) al principio de su flujo de trabajo puede ayudar a detectar estos problemas antes de que causen estragos.
Las 10 Principales Vulnerabilidades de Seguridad en Python
A continuación, se presentan diez de las vulnerabilidades de seguridad más comunes y peligrosas en aplicaciones Python. Para cada una, explicamos cómo surge la vulnerabilidad, el impacto potencial y cómo solucionarla o prevenirla, con una mención especial a cómo las herramientas de seguridad modernas pueden ayudar.
1. Ejecución de Código Arbitrario mediante eval() y exec()
de Python eval() (y su primo exec()) permite la ejecución dinámica de código Python a partir de cadenas. Utilizado de forma imprudente, eval puede convertir un script simple en una bomba de relojería. Si un atacante puede influir en la cadena pasada a eval(), pueden ejecutar cualquier código que deseen – desde imprimir datos hasta eliminar archivos o algo peor. Como dijo un desarrollador, “El problema con eval es que se corre el riesgo de permitir a los atacantes crear entradas maliciosas ... que engañan a tu programa para que ejecute código dañino”. En otras palabras, una eval(user_input) llamada podría funcionar hoy con entradas seguras, pero una entrada ingeniosamente elaborada mañana (como __import__('os').system('rm -rf \/')) podría borrar tu servidor.
Impacto: La ejecución arbitraria de código es tan grave como suena – significa un compromiso completo de la aplicación. Los atacantes podrían robar datos, modificarlos o tomar el control del host. Esto no es un caso extremo teórico: se han producido brechas reales por el uso inseguro de eval. Incluso si tu uso de eval es “seguo” hoy (por ejemplo, solo evaluando expresiones matemáticas), crea una vulnerabilidad latente que futuros cambios pueden activar accidentalmente.
Mitigación: Nunca uses eval o exec con entradas no confiables. De hecho, evítalos por completo a menos que sea absolutamente necesario. Python ofrece alternativas más seguras para la mayoría de los casos de uso (por ejemplo, utiliza la evaluación literal a través de ast.literal_eval para leer estructuras de datos, o tablas de despacho para llamar funciones en lugar de construir una cadena para eval). Valida o sanea siempre las entradas rigurosamente si se necesita ejecución dinámica.
Destacado de Aikido Security: Una buena herramienta de análisis estático (SAST) puede detectar el uso de funciones peligrosas. El escaneo de código de Aikido, por ejemplo, puede señalar instancias de eval() o exec() en tu código. Esto te ayuda a identificar rutas de código riesgosas durante la revisión de código o en CI – antes de antes de que lleguen a producción. Al integrar este tipo de escaneo en tu pipeline, recibirás una alerta cada vez que un nuevo uso de eval se introduzca, para que pueda ser refactorizado o examinado inmediatamente.
2. Inyección de Comandos del Sistema Operativo a través de subprocess o os.system
Siguiendo con el tema de la inyección de código, otro escollo crítico es inyección de comandos del sistema operativo. Esto ocurre cuando un programa Python pasa la entrada del usuario a comandos de shell del sistema sin la sanitización adecuada. Funciones como os.system(), subprocess.run() (con shell=True), o incluso el uso de backticks/popen pueden ejecutar comandos de shell. Si los datos controlados por el usuario se concatenan en esos comandos, un atacante puede inyectar comandos maliciosos. Por ejemplo:
# Vulnerable snippet:filename = input("Enter the filename to delete: ")os.system(f"rm -f {filename}")
Un atacante podría introducir file.txt; shutdown -h now y de repente nuestro script inocente ejecuta rm -f file.txt; shutdown -h now. En un incidente real, los atacantes explotaron una vulnerabilidad de MLflow que se originó al pasar datos no saneados a un os.system llamada dentro de una función predict. Como señalan los investigadores de seguridad de Snyk, la inyección de comandos permite la ejecución de comandos no autorizados, lo que lleva a filtraciones de datos, compromiso del sistema y otras actividades maliciosas.
Impacto: La inyección de comandos del sistema operativo puede ser devastadora. Los atacantes pueden encadenar comandos para robar archivos sensibles (por ejemplo, leer la configuración de la aplicación o credenciales), crear cuentas de usuario de puerta trasera o pivotar más profundamente en la red. Dado que estos comandos se ejecutan con los mismos privilegios que tu proceso Python, el impacto podría variar desde el volcado de una base de datos hasta la toma total del servidor. Las brechas habilitadas por inyecciones de comandos a menudo resultan en graves daños financieros y reputacionales.
Mitigación: Nunca concatenes directamente la entrada del usuario en comandos de shell. Si necesitas llamar a programas externos, utiliza las APIs más seguras: la de Python subprocess el módulo te permite pasar argumentos como una lista (lo que evita invocar un shell real). Por ejemplo, usa subprocess.run(["rm", "-f", filename]) en lugar de os.system("rm -f " + filename). Si el uso de subprocess con shell=True es inevitable, asegúrate de que la entrada esté estrictamente validada o saneada (por ejemplo, permitir solo nombres de archivo alfanuméricos esperados). Además, considera usar librerías de alto nivel para tareas del sistema operativo (por ejemplo, usa el manejo de archivos de Python en lugar de rm para eliminar un archivo).
Destacado de Aikido Security: El análisis estático de código de Aikido incluye reglas para detectar patrones de inyección de comandos. Te avisará si detecta algo como os.system(user_input) o subprocess.Popen(..., shell=True) con entrada variable. En la práctica, esto significa que tu pipeline de CI puede fallar una compilación si se introduce un uso peligroso. Al detectarlos durante el desarrollo, evitas ese momento de “vaya” en el que un fragmento de depuración olvidado o una llamada rápida y sucia a la shell se convierte en una puerta abierta para los atacantes.
3. Secretos Hardcodeados en el Código
La codificación de secretos —como claves API, credenciales, tokens o claves privadas— directamente en el código fuente es una práctica extendida y peligrosa. Es fácil de hacer («por ahora, solo pon la clave de AWS aquí…»), pero difícil de deshacer, ya que los secretos pueden persistir en el historial de commits incluso después de su eliminación. Los secretos expuestos son una mina de oro para los atacantes. De hecho, las filtraciones de secretos se han disparado recientemente: en 2022, un análisis de repositorios públicos de GitHub encontró 10 millones de secretos expuestos en solo 1 año (un aumento del 67% respecto al año anterior). Estos no solo son cometidos por desarrolladores junior, sino que ocurren en todos los niveles. Una vez filtrados, los secretos se han utilizado en grandes brechas de seguridad (por ejemplo, un atacante utilizó credenciales de administrador hardcodeadas en el código para vulnerar los sistemas internos de Uber).
Impacto: Si un atacante encuentra un secreto (en un repositorio público, una copia de seguridad filtrada, una imagen Docker, etc.), a menudo puede pivotar directamente a sus sistemas. Imagine una contraseña de base de datos hardcodeada: si se filtra, un atacante podría acceder a su base de datos y extraer datos de usuario. Las claves API de la nube hardcodeadas son aún peores: los proveedores de la nube informan que las credenciales expuestas suelen ser explotadas en cuestión de minutos tras su descubrimiento por bots que escanean repositorios. El radio de impacto puede incluir el compromiso total del entorno cloud, facturas de cloud elevadas para criptomineros o acceso no autorizado a servicios sensibles. Los secretos son realmente «las llaves del reino», por lo que filtrarlos es como dejar la puerta principal abierta a los ladrones.
Mitigación: Nunca haga commit de secretos reales en el código. Utilice archivos de configuración, variables de entorno o servicios de gestión de secretos (como HashiCorp Vault, AWS Secrets Manager, etc.) para inyectar secretos en tiempo de ejecución. Si debe incluir una credencial predeterminada o de ejemplo en el código, asegúrese de que sea un marcador de posición que no sea de producción. Realice escaneos regulares de secretos en su base de código e historial; si un secreto se cuela, rótelo inmediatamente (es decir, invalidarlo y reemplazarlo) y purgue el commit si es posible. Emplee el principio de mínimo privilegio: incluso si un secreto se filtra, un token de solo lectura es menos dañino que una clave de administrador completa.
Mención de Aikido Security: Aquí es donde brillan las herramientas de detección de secretos. El escaneo de secretos de Aikido (inspirado en herramientas como GitGuardian y otras) buscará automáticamente patrones como claves API, credenciales, certificados y más en sus repositorios. Puede alertarle en el momento en que un desarrollador comete accidentalmente un secreto. Integrar una herramienta así en su CI/CD (o incluso en los hooks de pre-commit) significa que una clave AWS hardcodeada es marcada antes de que llegue a GitHub. La plataforma de Aikido incluso ofrece remediación automatizada; por ejemplo, revocando la credencial expuesta y guiando al desarrollador para que utilice un método de almacenamiento seguro.
4. Deserialización Insegura (Abuso de Pickle)
El módulo pickle de Python proporciona una forma de serializar y deserializar objetos complejos. Sin embargo, pickle es notoriamente inseguro por diseño. Deserializar datos de una fuente no confiable puede ejecutar código arbitrario durante el proceso de deserialización. La propia documentación de Python es muy clara al respecto: «El módulo pickle no está diseñado para ser seguro contra datos maliciosos. Nunca deserialice datos recibidos de una fuente no confiable o no autenticada.» En la práctica, esto significa que si un atacante puede alimentar su aplicación con un pickle (por ejemplo, una cookie falsificada o un objeto en caché), podría ejecutar cualquier código Python en su servidor, esencialmente una vulnerabilidad de RCE (Ejecución Remota de Código) pre-autenticación.
Ejemplo real: Un desarrollador podría usar pickle.loads() en datos recibidos a través de una red (quizás pensando que es solo una forma conveniente de transmitir objetos Python). Los atacantes pueden crear una carga útil de pickle que, al ser deserializada, ejecuta comandos del sistema. Investigadores de seguridad han demostrado exploits triviales donde la deserialización de un objeto aparentemente inofensivo activa una shell inversa para el atacante. En un caso, un exploit de 15 líneas utilizando pickle fue suficiente para generar una shell, porque la aplicación confiaba ciegamente en los datos de pickle a través de la red.
Impacto: La deserialización insegura es un problema crítico, que suele conducir a la ejecución remota de código completa. Dado que pickle puede invocar cualquier clase y método durante la carga, un atacante puede abusar de ello para realizar acciones como eliminar archivos, instalar malware o pivotar a otros servicios internos. A diferencia de algunas vulnerabilidades que “simplemente” filtran datos, esto otorga un control directo al atacante. El impacto está limitado únicamente por los permisos de la aplicación en ejecución (y a menudo las aplicaciones Python se ejecutan con muchos privilegios).
Mitigación: No utilices pickle (o serialización similar como marshal) en datos de fuentes no confiables. Si necesitas intercambiar datos entre sistemas, utiliza formatos seguros como JSON o XML, e incluso entonces, sé cauteloso (valida las entradas). Para casos de uso específicos de Python (compartir objetos), considera jsonpickle en modo seguro u otros serializadores más seguros, pero asume siempre que la entrada puede ser hostil. Si absolutamente debes aceptar datos serializados con pickle (por ejemplo, por compatibilidad), trátalos como código: exige autenticación, utiliza firmas digitales para asegurar que provienen de una fuente confiable, o ejecuta la deserialización en un sandbox con privilegios restringidos. El consejo más simple suele ser el mejor: evitar por completo la deserialización dinámica de la entrada del usuario.
Destacado de Aikido Security: Los escáneres de código modernos pueden detectar el uso de APIs peligrosas como pickle.loads o pickle.load sobre datos potencialmente externos. El motor SAST de Aikido, por ejemplo, realiza comprobaciones de deserialización insegura – sabe que cualquier invocación de pickle es una señal de alarma a menos que se demuestre lo contrario. Las destacará durante las revisiones de código. Además, el escáner de dependencias de Aikido rastrea los CVE de deserialización conocidos. Por ejemplo, algunas bibliotecas (o frameworks) han tenido fallos de deserialización (no solo pickle, incluso cosas como XML o YAML, que cubriremos a continuación). Aikido te alertará si estás utilizando una versión vulnerable de dicha biblioteca y sugerirá actualizaciones o parches.
5. Carga Insegura de YAML (Vulnerabilidad de PyYAML)
Los problemas de serialización no se limitan a pickle. YAML, un formato de datos legible por humanos, también puede ser problemático si se usa de forma insegura. La popular biblioteca PyYAML tenía un fallo bien conocido: usar yaml.load() en una entrada no confiable podría ejecutar objetos Python arbitrarios. De hecho, la API de PyYAML load() era básicamente tan potente como pickle. CVE-2017-18342 se asignó a este problema: "En PyYAML anterior a la versión 5.1, la API yaml.load() podía ejecutar código arbitrario si se utilizaba con datos no confiables". La solución fue introducir un safe_load función y hacer load() por defecto al modo seguro en versiones más recientes. Pero muchas aplicaciones aún pueden usar inadvertidamente la antigua yaml.load (o una versión anterior de PyYAML), pensando que solo están parseando una configuración, cuando en realidad un atacante podría crear un YAML que ejecute código.
Impacto: Similar a pickle, el impacto es la ejecución remota de código. Por ejemplo, un archivo YAML puede incrustar un objeto Python de un tipo que, al construirse, ejecuta una llamada al sistema. Los atacantes que explotan esto pueden hacer que tu aplicación ejecute comandos del sistema en el momento en que intenta parsear un YAML malicioso. Esto puede explotarse a través de cualquier funcionalidad que parsea YAML proporcionado por el usuario (los escenarios comunes incluyen importadores de configuración, plantillas de Kubernetes en CI/CD o aplicaciones web que aceptan entrada YAML). El CVE mencionado fue marcado como Crítico (CVSS 9.8) precisamente por la facilidad con la que podía activarse y la gravedad del resultado.
Mitigación: Utilice siempre carga segura métodos para formatos de datos. En PyYAML, utilice yaml.safe_load() for any content that is not fully trusted. This mode only parses basic YAML types (strings, ints, lists, dicts) and will refuse to instantiate custom objects. If you are on PyYAML < 5.1, upgrade the library – the old load() es directamente inseguro. Del mismo modo, tenga cuidado con otros formatos de serialización: utilice json.loads (JSON no tiene ejecución de código por diseño, lo cual es bueno), o si se utiliza similar a pickle alternativas, asegúrate de que tengan un modo seguro. Una idea de defensa en profundidad: ejecuta el análisis en un entorno de bajos privilegios o sandbox si es posible (así, incluso si el código se ejecuta, no puede causar mucho daño).
Destacado de Aikido Security: análisis de dependencias is key here. Aikido’s dependency analyzer would flag that you have PyYAML <5.1 in your requirements, warning you of CVE-2017-18342 and advising an upgrade. On the code side, Aikido’s SAST can also catch usage of yaml.load(...) y sugiere usar safe_load en su lugar. Se trata de detectar el problema en dos niveles: asegúrate de que estás usando una versión segura de la librería y de que estás llamando a las funciones seguras. Al integrar estas comprobaciones, se te notificaría durante el desarrollo que “oye, esta llamada de análisis YAML no es segura” con orientación para solucionarlo.
6. Recorrido de directorios mediante operaciones de archivo inseguras (extracción de Tarfile)
El manejo de archivos es común en Python (por ejemplo, desempaquetar archivos subidos por el usuario). Pero las operaciones de archivo ingenuas pueden introducir vulnerabilidades de path traversal. Un ejemplo destacado es el módulo integrado de Python tarfile módulo. Se reveló que durante más de 15 años, tarfile.extractall() era vulnerable a un ataque de path traversal (denominado una “vulnerabilidad de 15 años” cuando fue redescubierta). Si un archivo tar malicioso contiene entradas de archivo con ../ en sus nombres, extractall escribiría archivos fuera del directorio previsto, sobrescribiendo potencialmente archivos críticos del sistema. Esto se rastrea como CVE-2007-4559. Aunque es una CVE antigua, sigue sin corregirse en la biblioteca estándar de Python a día de hoy, y una investigación de 2022 mostró cientos de miles de repositorios siguen utilizando tarfile de manera vulnerable.
Un atacante podría subir o proporcionar un archivo tar especialmente diseñado de tal manera que, cuando su código lo extrae, implanta una carga útil (por ejemplo, una web shell o una configuración sobrescrita) en una ubicación a la que el atacante no debería tener acceso. En una demostración, los investigadores explotaron esto para obtener ejecución de código sobrescribiendo el propio código de un paquete de Python después de la extracción.
No son solo los archivos tar; problemas similares pueden ocurrir con archivos zip (vulnerabilidad zip slip) o cualquier operación de archivo donde se construyen rutas a partir de entradas externas. Sin las comprobaciones adecuadas, una entrada de archivo llamada ../../../../../etc/passwd escribirá en /etc/passwd en la extracción.
Impacto: La escalada de rutas puede llevar a escritura arbitraria de archivos (o leer, en algunos casos) en el servidor. Escribir archivos en el lugar equivocado puede escalar a la ejecución de código, por ejemplo, sobrescribiendo la configuración de una aplicación para que apunte a código controlado por el atacante, o dejando un binario troyano en el PATH. Incluso si no conduce inmediatamente a RCE, sobrescribir archivos críticos puede sabotear la integridad del sistema o facilitar ataques posteriores. Considere las consecuencias de que un atacante sobrescriba la aplicación .env archivo o un script que se ejecuta; podrían insertar comandos maliciosos. En el caso de CVE-2007-4559, la comunidad elevó su gravedad cuando se demostró que la ejecución de código es a menudo una consecuencia directa de la explotación de la sobrescritura de archivos.
Mitigación: Nunca extraigas archivos de fuentes no confiables sin validación. Para tarfile, utiliza tarfile.extractall(path, members=...) y filtrar manualmente los miembros. Puedes implementar una verificación para asegurar que la ruta de archivo de ningún miembro esté fuera del directorio objetivo (por ejemplo, resolviendo rutas absolutas y buscando patrones de recorrido). La documentación de Python ahora incluye un fragmento de código para hacer esto de forma segura, esencialmente rechazando cualquier archivo con .. o prefijos de unidad. Alternativamente, considera usar librerías de terceros que realicen una extracción segura. Para archivos zip, inspecciona de manera similar los nombres antes de escribir, o usa librerías que mitiguen el zip slip. Siempre con el principio de mínimo privilegio: si es posible, ejecuta la extracción en un sandbox o en un directorio con permisos limitados. De esa manera, incluso si un exploit intenta escapar, no podrá afectar archivos críticos del sistema.
Destacado de Aikido Security: El análisis estático de Aikido puede detectar patrones peligrosos como tarfile.extractall() uso sin un filtrado seguro de miembros. Conoce CVE-2007-4559 y puede marcar instancias en tu código donde llamas a extractall o extract sin precauciones. Esto sirve como un aviso para implementar las verificaciones adecuadas. Además, el feed de inteligencia de vulnerabilidades de Aikido (su investigación interna) rastrea estos problemas “escondidos” tanto en librerías estándar como en paquetes populares. Al escanear tu código y dependencias, mostrará una advertencia como “Posible path traversal en el uso de tarfile – considera validar el contenido del archivo”, enlazando a la guía de mejores prácticas. En resumen, ayuda a los desarrolladores a detectar un error de hace 15 años que de otro modo podría pasar desapercibido en su código.
7. Uso de librerías obsoletas con vulnerabilidades conocidas (Requests, urllib3, etc.)
El rico ecosistema de paquetes de Python es una de sus fortalezas, pero cada dependencia que incluyes también puede introducir vulnerabilidades si no se mantiene actualizada. Librerías Python de alto perfil han tenido su cuota de CVEs. Por ejemplo, la ampliamente utilizada librería HTTP Requests ha tenido errores como:
- CVE-2023-32681 – donde Requests filtraría credenciales de proxy HTTP en ciertos escenarios de redirección, enviando
Proxy-Authorizationcabeceras a servidores de destino y exponiendo potencialmente información sensible info. (Esto se corrigió en Requests 2.31.0.) - CVE-2024-35195 – un fallo lógico por el cual, si se deshabilitaba la verificación de certificados SSL una vez en una Sesión, Requests persistiría ignorando la verificación para solicitudes posteriores al mismo host, incluso si se intentaba volver a habilitarla. Esencialmente, una vez roto, siempre roto – una desagradable sorpresa que podría dejar las conexiones inseguras de forma silenciosa. Corregido en 2.32.0.
- Inyección CRLF en urllib3 – urllib3 (utilizado por Requests internamente) tenía una vulnerabilidad que permitía la inyección CRLF en las cabeceras HTTP si un atacante controlaba parte de la URL o el método de la solicitud (por ejemplo, se podían insertar caracteres de nueva línea en una cabecera). Esto podría ser explotado para contrabandear cabeceras o dividir respuestas, lo que podría llevar al secuestro de sesiones o a la manipulación de cachés web. (Se asignaron múltiples CVE, por ejemplo, CVE-2019-9740 para el urllib incorporado de Python, a este tipo de problemas.)
Estos son solo algunos ejemplos. Otros destacables incluyen vulnerabilidades en análisis de URL (por ejemplo, CVE-2023-24329, un problema en urllib.parse que permitía a los atacantes eludir las listas negras de URL utilizando una URL engañosa que comenzaba con un espacio o un carácter de control), y problemas en gestión de paquetes herramientas (como la vulnerabilidad pasada de pip CVE-2018-20225 que permitía a una descarga maliciosa sobrescribir archivos arbitrarios). Incluso las bibliotecas de datos populares (Pandas, NumPy) ocasionalmente tienen correcciones de seguridad (a menudo problemas de DoS o corrupción de memoria).
Impacto: Las vulnerabilidades conocidas en las bibliotecas pueden tener un amplio rango de impactos, desde la fuga de información y la denegación de servicio hasta el compromiso total, dependiendo del error. El punto clave es que los atacantes buscan activamente aplicaciones que utilizan versiones desactualizadas. Si estás ejecutando una versión antigua de Requests y tu aplicación realiza llamadas de red, un atacante podría explotar la fuga de autenticación del proxy para robar credenciales. O si tienes un urllib3 desactualizado, podrían explotar la inyección CRLF para envenenar tus interacciones HTTP. Dado que estas vulnerabilidades son públicas, los atacantes saben exactamente qué buscar. No actualizar las dependencias es como dejar agujeros conocidos sin parchear en tu aplicación.
Mitigación: Mantente al tanto de las actualizaciones de dependencias y los avisos de seguridad. Utiliza herramientas para verificar CVEs conocidos en tus dependencias (pip-tools, Dependabot de GitHub, o herramientas SCA comerciales). Cuando se anuncia un nuevo CVE crítico (por ejemplo, un error grave en Django, Flask, Requests, etc.), prioriza la actualización a una versión corregida. Siempre que sea posible, fija tus dependencias a versiones conocidas y revisa los changelogs de las actualizaciones. También considera fijar versiones mínimas: por ejemplo, requiere requests>=2.31.0 si sabes que las versiones anteriores son vulnerables. Además, emplea una defensa en profundidad: por ejemplo, incluso si Requests tuviera un error de verificación de certificados, podrías añadir una capa de seguridad de red (como la intercepción TLS o el 'certificate pinning' adicional) para mitigar el riesgo. Pero lo más sencillo es: mantén tus paquetes actualizados.
Destacado de Aikido Security: Aquí es donde entra en juego el análisis de composición de software (SCA) en Aikido. El escáner de dependencias de Aikido verifica continuamente los requisitos de tu proyecto (e incluso tus dependencias transitivas) contra una base de datos de vulnerabilidades conocidas. Te alertará: “La librería X versión Y es vulnerable a CVE-2023-32681”, a menudo proporcionando detalles y una versión corregida recomendada. Aún mejor, la plataforma de Aikido puede realizar soluciones automatizadas, por ejemplo, abriendo un pull request para actualizar una versión. Al integrar esto en tu CI/CD, te aseguras de no desplegar contenedores o aplicaciones con librerías desactualizadas. En resumen, herramientas como Aikido te ayudan a detectar CVEs conocidos e incluso a remediarlos automáticamente, complementando los escáneres de código abierto con reglas personalizadas e inteligencia actualizada.
8. Desactivación de Funcionalidades de Seguridad (p. ej., Ignorar la Validación de Certificados SSL)
A veces, durante el desarrollo o las pruebas, los ingenieros desactivan importantes comprobaciones de seguridad, y luego el código se envía a producción de esa manera. Un ejemplo clásico en Python es realizar solicitudes HTTP con verificación SSL deshabilitada. La librería Requests (y otras como urllib3) permiten un verify=False flag para ignorar errores de certificado SSL. Esto es conveniente al tratar con certificados autofirmados en un entorno de desarrollo, pero si llega a producción, te expone a ataques de intermediario (MitM). La documentación de Requests advierte explícitamente: cuando verify=False, el cliente aceptará cualquier certificado – ignorando las discrepancias de nombre de host o los certificados caducados/autofirmados – haciendo que tu aplicación sea vulnerable a ataques MITM. Esencialmente, ya no confirmas que estás hablando con el servidor real. Un atacante podría interceptar tu tráfico (especialmente si puede suplantar DNS o está en la misma red) y presentar cualquier certificado para secuestrar la conexión.
Más allá de la verificación SSL, otras características deshabilitadas también pueden causarte problemas: por ejemplo, usar HTTP en lugar de HTTPS para comunicaciones sensibles, desactivar manualmente la comprobación de nombre de host TLS o deshabilitar las redes de seguridad integradas de Python (como ejecutar el intérprete con las aserciones desactivadas si esas aserciones estaban protegiendo una comprobación crítica de seguridad).
Impacto: Si un atacante puede interponerse en tu tráfico (lo cual es más fácil de lo que muchos piensan, especialmente en redes en la nube o de contenedores, o Wi-Fi público, etc.), puede descifrar y modificar datos en tránsito cuando la verificación del certificado está desactivada. Esto podría significar que un atacante roba tokens de API, inyecta respuestas maliciosas o suplanta servicios. En una arquitectura de microservicios, un servicio comprometido o un punto de apoyo en la red podría permitir el pivoteo interceptando llamadas de un servicio a otro si esas llamadas no están debidamente verificadas. También hemos visto escenarios en los que desactivar la verificación SSL en una librería cliente de API permitió a los atacantes servir malware desde lo que debería haber sido un servidor de actualizaciones de confianza, simplemente usando un certificado autofirmado en un dominio suplantado. Las consecuencias pueden ser datos robados, toma de control de cuentas o inyección de datos/comandos maliciosos en el funcionamiento de tu aplicación.
Mitigación: Nunca desactives la verificación SSL en código de producción. Si realmente debes hacerlo (por ejemplo, conectarte a un servicio interno con un certificado autofirmado), entonces al menos fija el certificado o la huella digital para no confiar ciegamente en cualquier certificado. Mejor aún, usa una CA privada y haz que tus certificados internos sean de confianza proporcionando el paquete de CA. Durante el desarrollo, si utilizas verify=False, sé extremadamente cauteloso al eliminarlo antes de hacer commit o usa la configuración para deshabilitarlo solo en entornos que no sean de producción. Las revisiones de código deberían tratar verify=False como una gran señal de alerta. Además, considera usar herramientas como linters para prohibir esta bandera en el código comprometido. Lo mismo ocurre con otros atajos; por ejemplo, no captures e ignores excepciones de seguridad, no desactives la autenticación o CSRF en depuración y olvides volver a habilitarlas, etc.
Destacado de Aikido Security: Los escáneres de Aikido pueden detectar el uso de configuraciones de riesgo como verify=False en las solicitudes. De hecho, puede aplicar políticas (similar a cómo funcionan las políticas de Prisma/Checkov) para garantizar que ningún código deshabilite las comprobaciones de certificados. Si se encuentra un patrón de este tipo, Aikido lo señalará en los resultados del escaneo, tratándolo como una vulnerabilidad. Podría decir «Verificación de certificado SSL deshabilitada aquí – esto no debería usarse en producción» con un enlace a la línea de código. Al integrar esto en CI, se previene eficazmente el escenario de «dejé una bandera de depuración activada». Además, el análisis de dependencias mencionado anteriormente también le informará sobre errores como CVE-2024-35195, donde la desactivación no intencionada persiste, destacando que necesita actualizar su biblioteca para restaurar completamente la seguridad.
9. Paquetes Maliciosos o Comprometidos (ataques a la cadena de suministro)
No todas las amenazas provienen de errores en su propio código; a veces el código que extrajo de PyPI en sí es el ataque. ataques a la cadena de suministro a través de paquetes Python maliciosos han aumentado en los últimos años. Los atacantes publican paquetes con errores tipográficos o directamente falsos en PyPI, nombrándolos de forma similar a librerías populares (p. ej., solicitudes en lugar de requests) o nombres atractivos como free-net-vpn. Desarrolladores desprevenidos los instalan, y el código malicioso se ejecuta durante la instalación (en setup.py) o al importarlos. Por ejemplo, en 2022, se descubrieron diez paquetes PyPI maliciosos que suplantaron librerías comunes e inyectaron malware de robo de información en los sistemas de los desarrolladores. Un paquete, imitando uno muy conocido, fue diseñado para buscar credenciales de AWS y enviarlas a un atacante a través de un webhook de Discord. Otro llamado WINRPCexploit afirmaba ser una herramienta de exploit pero en realidad exfiltró todas las variables de entorno (a menudo conteniendo secretos) al atacante.
Incluso los paquetes legítimos pueden verse comprometidos si un atacante obtiene acceso a la cuenta del mantenedor (como ocurrió con event-stream en Node.js, y también en Python). Ha habido casos en los que paquetes ampliamente utilizados fueron secuestrados brevemente para incluir código malicioso en nuevas versiones. Por ejemplo, un paquete podría empezar de repente a enviar telemetría de uso (espionaje) o incluir una puerta trasera que se activa bajo ciertas condiciones.
Impacto: El impacto de los paquetes maliciosos puede ser grave y de gran alcance. Dado que estos paquetes a menudo ejecutan código en el momento de la instalación (a través de scripts de configuración) o al ser importados, pueden hacer cualquier cosa que tu usuario pueda hacer: robar credenciales (de archivos de configuración, variables de entorno, configuración de AWS CLI, etc.), instalar troyanos, cifrar archivos (ransomware) o abrir shells inversas. Y debido a que a menudo se ejecutan con los privilegios del desarrollador, pueden comprometer no solo la aplicación, sino todo el sistema o las credenciales que tiene ese desarrollador. En un pipeline de CI, un paquete envenenado podría exfiltrar secretos de tu entorno de CI. En un entorno de producción, una dependencia maliciosa podría convertirse en una puerta trasera para que los atacantes la exploten continuamente. Los ataques a la cadena de suministro son particularmente insidiosos porque socavan la confianza en las mismas herramientas que utilizamos.
Mitigación: Examina cuidadosamente y monitoriza tus dependencias. Algunas mejores prácticas: Fija las dependencias a versiones específicas (para no descargar automáticamente una actualización manipulada). Verifica las sumas de comprobación o las firmas de los paquetes (PyPI ahora soporta la firma GPG y similares, aunque no todos los paquetes la usan). Usa herramientas como pip install --no-build-isolation --only-binary=:all: para wheels conocidos y buenos para evitar ejecutar setup.py de paquetes desconocidos. Considera emplear un proxy o espejo local de PyPI y solo permitir el paso de paquetes verificados. Lee siempre los detalles del paquete: si requests tiene 100 millones de descargas y de repente hay una nueva solicitudes paquete, lo cual es sospechoso. Las características de seguridad de PyPI (como la 2FA para los mantenedores) están mejorando, pero la responsabilidad también recae en los usuarios: no instales paquetes aleatorios sin escrutinio. Si es posible, revisa el origen de las nuevas dependencias (al menos un escaneo rápido en busca de patrones maliciosos obvios como os.system('curl ...')).
Destacado de Aikido Security: La plataforma de Aikido aborda los riesgos de la cadena de suministro de múltiples maneras. Primero, su análisis de dependencias marcará los paquetes que se sabe que son malware o que tienen un comportamiento sospechoso. Aikido ingiere continuamente inteligencia de amenazas (indicadores de paquetes maliciosos) y puede advertirte, “El paquete X ha sido reportado como malicioso; no lo uses.” En segundo lugar, Aikido SafeChain (una herramienta de código abierto de Aikido) puede evitar la instalación de paquetes no confiables. Básicamente, bloquea paquetes que no han envejecido lo suficiente o que aparecen repentinamente en su árbol de dependencias, mitigando ataques de typosquatting al requerir un período de «enfriamiento» (por ejemplo, solo instalar paquetes que hayan existido durante más de 24 horas, para evitar malware recién publicado). Al integrar estas defensas, añade un guardián automatizado cada vez que realiza pip install. En efecto, Aikido ayuda a garantizar que las bibliotecas de las que depende no le traicionen, detectando actores maliciosos conocidos y aplicando políticas para dificultar la entrada de nuevos.
10. Prácticas Criptográficas Débiles (Aleatoriedad Insuficiente y Mal Uso de la Criptografía)
La criptografía es compleja, y usarla incorrectamente puede socavar la seguridad tanto como un error directo. En Python, un error común es usar un generador de números aleatorios incorrecto para contextos sensibles a la seguridad. Por ejemplo, los desarrolladores podrían usar random.random() o random.randrange() para generar un token de restablecimiento de contraseña o un ID de sesión. Sin embargo, el estándar aleatorio módulo es no es criptográficamente seguro – utiliza algoritmos predecibles no adecuados para la generación de secretos. Python introdujo el secretos módulo por esta razón, afirmando que secretos debe utilizarse para la aleatoriedad crítica de seguridad en lugar del generador de números pseudoaleatorios predeterminado en el aleatorio módulo, que está diseñado para modelado y simulación, no para seguridad o criptografía. Si utiliza aleatorio para tokens, un atacante podría predecir valores (dadas suficientes observaciones o conocimiento de la semilla).
Otra práctica débil es el uso de algoritmos obsoletos o débiles. Por ejemplo, usar MD5 o SHA1 para el hash de contraseñas (ambos se consideran rotos para este propósito) o no salar las contraseñas en absoluto. O crear su propia «cifrado» (por ejemplo, un esquema XOR casero) en lugar de usar librerías probadas. Estas debilidades podrían no manifestarse como una CVE en su código, pero reducen significativamente la barrera para los atacantes. Un ejemplo real: si las contraseñas se almacenan sin sal con SHA-1, un atacante que robe la base de datos de hashes puede descifrar la mayoría de las contraseñas rápidamente utilizando tablas rainbow precalculadas.
Impacto: La aleatoriedad insegura podría llevar al secuestro de cuentas o a la falsificación de tokens. Imagine que un atacante adivina las cookies de sesión porque se derivaron de una salida predecible de PRNG; esto no es solo teoría, ha sucedido en sistemas mal diseñados. Un hashing o cifrado débil significa que si un atacante viola una capa (por ejemplo, obtiene acceso de lectura a una base de datos o intercepta tráfico cifrado), puede descifrar o romper fácilmente los datos supuestamente protegidos. En general, la criptografía débil da una falsa sensación de seguridad; los atacantes podrían no necesitar un “exploit” cuando pueden simplemente forzar o predecir lo que necesitan.
Mitigación: Sigue las actuales mejores prácticas criptográficas. Utiliza el secretos módulo o os.urandom() para generar tokens, claves o nonces secretos. Utiliza librerías de alto nivel para el cifrado (como Fernet en el criptografía librería) en lugar de escribir tu propia criptografía. Para el almacenamiento de contraseñas, utiliza KDFs establecidos (bcrypt, Argon2, PBKDF2 con iteraciones adecuadas) – nunca hashes planos sin sal. Mantén los algoritmos actualizados; por ejemplo, SHA-256 es bueno para la integridad, pero para contraseñas aún querrías un hash lento como bcrypt. Y siempre habilita TLS para la comunicación de red, utilizando protocolos modernos (TLS 1.2+). Esencialmente, confía en implementaciones bien probadas en lugar de enfoques personalizados o heredados. Además, mantente al tanto de las deprecaciones: si una configuración o cifrado SSL/TLS ya no se recomienda (el módulo ssl de Python suele actualizar los valores predeterminados, pero ten cuidado si anulas la configuración).
Destacado de Aikido Security: El SAST de Aikido puede ayudar a detectar algunas prácticas criptográficas desaconsejables; por ejemplo, puede advertir si detecta el aleatorio módulo utilizado donde secretos sería apropiado, o si encuentra MD5 siendo utilizado en un contexto de seguridad. Además, la inteligencia de vulnerabilidades de Aikido le alertará si está utilizando un algoritmo débil conocido en un contexto que tuvo CVEs anteriores (por ejemplo, si una biblioteca que utiliza por defecto un modo inseguro, Aikido podría señalarlo). Aunque la supervisión humana es crucial en criptografía, tener una herramienta automatizada para verificar no está de más. Es como un linter para la seguridad: si usted confirma inadvertidamente token = random.random(), una herramienta puede decir “¿Está seguro? Esto no es criptográficamente seguro.” Este tipo de retroalimentación temprana puede guiar a los desarrolladores hacia los módulos correctos (quizás incluso sugiriendo: import secrets y secrets.token_hex() como alternativa). En resumen, Aikido ayuda a hacer cumplir la higiene criptográfica al detectar patrones débiles obvios y manteniéndole informado sobre las vulnerabilidades relacionadas con la criptografía en el ecosistema.
Conclusión: Proteja su Código Python desde el Principio
La flexibilidad y el rico ecosistema de Python pueden ser un arma de doble filo: potente en manos de los desarrolladores, pero ofreciendo muchas vías para los atacantes si no se toman las precauciones adecuadas. Hemos explorado cómo elementos como la ejecución dinámica de código, las vulnerabilidades de inyección, los secretos filtrados, las dependencias desactualizadas y otras trampas pueden socavar la seguridad de sus aplicaciones Python. La buena noticia es que cada uno de estos fallos es prevenible con una combinación de mejores prácticas y las herramientas adecuadas:
- Adopte prácticas de codificación segura: valide las entradas, evite funciones peligrosas y utilice valores predeterminados seguros (por ejemplo, cargadores seguros, aleatoriedad segura, algoritmos actualizados).
- Mantén tus dependencias actualizadas y verificadas: no dejes que las CVEs conocidas persistan en tus requisitos y sé cauteloso con lo que instalas.
- Integra la seguridad en tu pipeline de desarrollo: esto significa ejecutar análisis estáticos, escaneo de secretos y auditorías de dependencias mientras codificas y durante la CI, no después de un incidente.
Las plataformas modernas de DevSecOps como Aikido pueden facilitar mucho este proceso al automatizar la detección de estos problemas, desde detectar una contraseña codificada antes de que salga de tu portátil, hasta bloquear el despliegue de un paquete vulnerable, o alertarte de un fallo recién revelado en una de tus librerías. Como señaló el informe de seguridad de contenedores de Red Hat, casi la mitad de los equipos pierden el sueño por la seguridad de los contenedores (y, por extensión, del software); lo mismo ocurre probablemente con la seguridad de las aplicaciones Python. Al desplazar la seguridad a la izquierda, es decir, abordarla temprano en el desarrollo, puedes reducir significativamente las posibilidades de una emergencia nocturna causada por un error de Python evitable.
En resumen, mantenerse informado y proactivo es clave. Sigue aprendiendo sobre codificación segura (la Guía de Seguridad de Python y los recursos de OWASP son excelentes), fomenta las revisiones de código con la seguridad en mente y aprovecha las herramientas automatizadas como tu red de seguridad. Los desarrolladores de Python que integran la seguridad desde el principio ahorrarán a sus organizaciones tiempo, dinero y reputación a largo plazo. Escribamos código que no solo sea ingenioso y limpio, sino también seguro y protegido. Tu yo futuro (y tus usuarios) te lo agradecerán.
Sigue leyendo:
Las 9 principales vulnerabilidades de seguridad de contenedores Docker
Las 7 principales vulnerabilidades de seguridad en la nube
Las 10 principales vulnerabilidades de seguridad de aplicaciones web que todo equipo debería conocer
Las 9 principales vulnerabilidades y configuraciones erróneas de seguridad Kubernetes
Las principales vulnerabilidades de seguridad de código encontradas en aplicaciones modernas Las principales vulnerabilidades de seguridad de JavaScript en aplicaciones web modernas
Las 9 principales vulnerabilidades de seguridad de la cadena de suministro de software explicadas

