Mejores prácticas para la ejecución de código no confiable

31

Tengo un proyecto en el que necesito permitir a los usuarios ejecutar código python arbitrario y no confiable ( un poco como este ) en mi servidor. Soy bastante nuevo en Python y me gustaría evitar cometer errores que introduzcan agujeros de seguridad u otras vulnerabilidades en el sistema. ¿Hay algunas mejores prácticas disponibles, lecturas recomendadas u otros consejos que pueda darme para que mi servicio sea utilizable pero no abusivo?

Esto es lo que he considerado hasta ahora:

  • Eliminar __builtins__del execcontexto para prohibir el uso de paquetes potencialmente peligrosos como os. Los usuarios solo podrán usar los paquetes que les proporcione.
  • Use hilos para hacer cumplir un tiempo de espera razonable.
  • Me gustaría limitar la cantidad total de memoria que se puede asignar dentro del execcontexto, pero no estoy seguro de si es posible.

Hay algunas alternativas a una escalera exec, pero no estoy seguro de cuál de ellas sería útil aquí:

  • Usar un ast.NodeVisitorpara detectar cualquier intento de acceder a objetos inseguros. ¿Pero qué objetos debería prohibir?
  • Buscando cualquier subrayado doble en la entrada. (menos elegante que la opción anterior).
  • Usando PyPyo algo similar a sandbox el código.

NOTA: Soy consciente de que hay al menos un intérprete basado en JavaScript. Eso no funcionará en mi escenario.

pswg
fuente
3
@MartijnPieters: Excelente. Probablemente digno de una respuesta, si resume cada uno.
Robert Harvey
Considere también: basura en el disco, red (no permita que envíen spam o lo que sea), permisos a otros archivos (leer sus archivos). Incluso expulsarlo mientras el bucle puede destruir la mecánica del CD ... Iría por la virtualización (cárceles o algunos kvm, lo que sea) o al menos un usuario sin casi privilegios. Establezca una cantidad razonable de memoria para aprovechar sus propios programas.
kyticka
1
Pruebe PyPy :> Sandboxing: PyPy proporciona la capacidad de ejecutar código no confiable de una manera totalmente segura.
Vorac

Respuestas:

28

El sandboxing de Python es difícil . Python es inherentemente introspectable, en múltiples niveles.

Esto también significa que puede encontrar los métodos de fábrica para tipos específicos de esos tipos y construir nuevos objetos de bajo nivel, que el intérprete ejecutará directamente sin limitación.

Estos son algunos ejemplos de cómo encontrar formas creativas de salir de los entornos limitados de Python:

La idea básica es siempre encontrar una manera de crear tipos básicos de Python; funciones y clases y salir del shell haciendo que el intérprete de Python ejecute bytecode arbitrario (¡sin marcar!).

Lo mismo y más se aplica a la execdeclaración ( exec()función en Python 3).

Entonces tu quieres:

  • Controle estrictamente la compilación de bytes del código de Python, o al menos procese el código de bytes para eliminar cualquier acceso a los nombres que comienzan con guiones bajos.

    Esto requiere un conocimiento profundo de cómo funciona el intérprete de Python y cómo está estructurado el bytecode de Python. Los objetos de código están anidados; el código de bytes de un módulo solo cubre el nivel superior de las declaraciones, cada función y clase consta de su propia secuencia de código de bytes más metadatos, que contienen otros objetos de código de bytes para funciones y clases anidadas, por ejemplo.

  • Debe incluir en la lista blanca los módulos que se pueden usar. Cuidadosamente.

    Un módulo de Python contiene referencias a otros módulos. Si importa os, hay un nombre local osen el espacio de nombres de su módulo que se refiere al osmódulo. Esto puede llevar a un atacante determinado a módulos que pueden ayudarlo a salir de la caja de arena. El picklemódulo, por ejemplo, le permite cargar objetos de código arbitrario, por ejemplo, por lo que si alguna ruta a través de los módulos incluidos en la lista blanca conduce al picklemódulo, aún tiene un problema.

  • Debe limitar estrictamente las cuotas de tiempo. Incluso el código más castrado puede intentar ejecutarse para siempre, atando sus recursos.

Eche un vistazo a RestrictedPython , que intenta darle el estricto control de bytecode. RestrictedPythontransforma el código de Python en algo que le permite controlar qué nombres, módulos y objetos están permitidos en Python 2.3 a 2.7.

Si RestrictedPythones lo suficientemente seguro para sus propósitos depende de las políticas que implemente. No permitir el acceso a nombres que comienzan con un guión bajo y estrictamente incluir en la lista blanca los módulos sería un comienzo.

En mi opinión, la única opción verdaderamente robusta es usar una máquina virtual separada, una sin acceso a la red al mundo exterior que destruyes después de cada ejecución. Cada nuevo script recibe una nueva VM en su lugar. De esa manera, incluso si el código logra salir de su entorno limitado de Python (lo cual no es improbable), todo el atacante tiene acceso de corta duración y sin valor.

Martijn Pieters
fuente
10

TL; DR Utilice un chroot / jail y ejecútelo como un usuario personalizado sin ningún privilegio.

La mejor práctica para ejecutar código no confiable es segregarlo a través de un sandbox del sistema . Para mayor seguridad:

  • crear un contenedor con solo Python y sus dependencias y las dependencias del contenedor
  • crear un contenedor sin todos los dispositivos que no son absolutamente necesarios (es decir, red y almacenamiento)
  • crear un contenedor con restricciones en la memoria y el uso del proceso
  • recrear el contenedor con cada ejecución (o al menos con cada usuario único y período de tiempo máximo)
  • ejecutarse como un usuario con el menor privilegio necesario
  • ejecutarse como un usuario que no tiene los permisos para escribir archivos

También sigue las prácticas estándar para ejecutar cosas de forma segura en un chroot. Puede reconstruir el sistema de archivos del chroot con cada llamada, también es particularmente paranoico. Por lo general, solo hace que el usuario no pueda realizar modificaciones en el sistema de archivos en el que se ejecuta chroot.

dietbuddha
fuente
Esto es lo único en lo que vas a estar incluso remotamente seguro de que lo has hecho bien: dale su propio proceso.
Michael Kohne
3

No hay forma de que pueda hacer esto de manera segura.

Si quisieras hacer algo como esto de manera segura, tendrías que comenzar por tener tu propia implementación de python que se ejecute en un entorno completamente controlado, preferiblemente en el navegador de los usuarios en lugar de en tu sistema. Puede comenzar con Jython (python para java) y empaquetarlo como un applet de java. Dado que se estaría ejecutando en el sandbox de Java, en la máquina del usuario, su sistema sería razonablemente seguro.

ddyer
fuente
44
La cuestión de la seguridad era para su servidor, no para la máquina del cliente. Los posibles riesgos de seguridad de Java, como los de cualquier otra tecnología web, son que el servidor podría usarse para implementar programas peligrosos para el cliente.
ddyer
1
@grasGendarme, al igual que las nuevas historias sobre accidentes de avión, en realidad te dicen mucho sobre lo raros que son; Las historias sobre los agujeros de seguridad de Java le dicen que Java es relativamente seguro. Nunca obtendrías tal historia sobre C porque la respuesta que obtendrías sería "bueno, duh; si la ejecutas, hará lo que quiera"
Richard Tingle
2

Como dijo Martijn anteriormente, esto es muy, muy difícil en Python. Sin rodeos porque Python es tan introspectable, no creo que sea posible limitando las características del lenguaje. Y si obtiene un sandbox que funcione para una versión de Python, existe la posibilidad de que la próxima versión lo rompa.

Echaría un vistazo a PyPy en lugar de CPython estándar. En resumen, es una implementación alternativa compatible de Python. Tiene varias ventajas y características distintas, y una de ellas es el sandboxing mediante la sustitución de llamadas del sistema en lugar de limitar las características del lenguaje.

James
fuente
0

Mientras el rendimiento no sea enormemente importante para usted, siempre puede ejecutarlo en Brython, que lo coloca efectivamente en el entorno limitado de JavaScript

Ian grande
fuente