¿Cómo puedo aislar Python en Python puro?

Resuelto Blixt asked hace 14 años • 7 respuestas

Estoy desarrollando un juego web en Python puro y quiero disponer de algunas secuencias de comandos simples para permitir un contenido de juego más dinámico. Los usuarios privilegiados pueden agregar contenido del juego en vivo.

Sería bueno si el lenguaje de programación pudiera ser Python. Sin embargo, no puede ejecutarse con acceso al entorno en el que se ejecuta el juego, ya que un usuario malintencionado podría causar estragos, lo cual sería malo. ¿Es posible ejecutar Python en un espacio aislado en Python puro?

Actualización : de hecho, dado que la verdadera compatibilidad con Python sería excesiva, un lenguaje de programación simple con sintaxis Pythonic sería perfecto.

Si no hay intérpretes de secuencias de comandos Pythonic, ¿hay otros intérpretes de secuencias de comandos de código abierto escritos en Python puro que pueda usar? Los requisitos son soporte para variables, condicionales básicos y llamadas a funciones (no definiciones).

Blixt avatar Jun 18 '10 15:06 Blixt
Aceptado

Esto no es realmente trivial.

Hay dos formas de proteger Python. Una es crear un entorno restringido (es decir, muy pocos globales, etc.) y execsu código dentro de este entorno. Esto es lo que sugiere Messa. Es agradable, pero hay muchas formas de salir del entorno limitado y crear problemas. Hubo un hilo sobre esto en Python-dev hace aproximadamente un año en el que la gente hacía cosas, desde detectar excepciones y hurgar en el estado interno hasta llegar a la manipulación del código de bytes. Este es el camino a seguir si quieres un lenguaje completo.

La otra forma es analizar el código y luego usar el astmódulo para eliminar construcciones que no desea (por ejemplo, declaraciones de importación, llamadas a funciones, etc.) y luego compilar el resto. Este es el camino a seguir si desea utilizar Python como lenguaje de configuración, etc.

Otra forma (que puede que no funcione para usted ya que está usando GAE) es el entorno de pruebas de PyPy . Si bien no lo he usado yo mismo, se dice en los intertubes que es el único Python real en espacio aislado que existe.

Según su descripción de los requisitos (los requisitos son soporte para variables, condicionales básicos y llamadas a funciones (no definiciones)), es posible que desee evaluar el enfoque 2 y eliminar todo lo demás del código. Es un poco complicado pero factible.

Noufal Ibrahim avatar Jun 18 '2010 09:06 Noufal Ibrahim

Aproximadamente diez años después de la pregunta original, Python 3.8.0 viene con auditoría . ¿Puede ayudar? Limitemos la discusión a la escritura en el disco duro para simplificar, y veamos:

from sys import addaudithook
def block_mischief(event,arg):
    if 'WRITE_LOCK' in globals() and ((event=='open' and arg[1]!='r') 
            or event.split('.')[0] in ['subprocess', 'os', 'shutil', 'winreg']): raise IOError('file write forbidden')

addaudithook(block_mischief)

Hasta ahora execpodría escribir fácilmente en el disco:

exec("open('/tmp/FILE','w').write('pwned by l33t h4xx0rz')", dict(locals()))

Pero podemos prohibirlo a voluntad, para que ningún usuario malintencionado pueda acceder al disco desde el código proporcionado en exec(). A los módulos Pythonic les gusta numpyo pickleeventualmente usan el acceso a archivos de Python, por lo que también tienen prohibido escribir en disco. Las llamadas a programas externos también se han deshabilitado explícitamente.

WRITE_LOCK = True
exec("open('/tmp/FILE','w').write('pwned by l33t h4xx0rz')", dict(locals()))
exec("open('/tmp/FILE','a').write('pwned by l33t h4xx0rz')", dict(locals()))
exec("numpy.savetxt('/tmp/FILE', numpy.eye(3))", dict(locals()))
exec("import subprocess; subprocess.call('echo PWNED >> /tmp/FILE', shell=True)",     dict(locals()))

Un intento de eliminar el bloqueo desde dentro exec()parece inútil, ya que el gancho de auditoría utiliza una copia diferente a localsla que no se puede acceder con el código ejecutado por exec. Por favor, demuéstrame que estoy equivocado.

exec("print('muhehehe'); del WRITE_LOCK; open('/tmp/FILE','w')", dict(locals()))
...
OSError: file write forbidden

Por supuesto, el código de nivel superior puede habilitar la E/S de archivos nuevamente.

del WRITE_LOCK
exec("open('/tmp/FILE','w')", dict(locals()))

El sandboxing dentro de Cpython ha demostrado ser extremadamente difícil y muchos intentos anteriores han fracasado. Este enfoque tampoco es del todo seguro, por ejemplo, para el acceso web público:

  1. Quizás Cpython no pueda auditar los módulos compilados hipotéticos que utilizan llamadas directas al sistema operativo; se recomienda incluir en la lista blanca los módulos Python puros seguros.

  2. Definitivamente todavía existe la posibilidad de que el intérprete Cpython se bloquee o se sobrecargue.

  3. Quizás también queden algunas lagunas para escribir los archivos en el disco duro. Pero no pude utilizar ninguno de los trucos habituales de evasión del sandbox para escribir un solo byte. Podemos decir que la "superficie de ataque" del ecosistema Python se reduce a una lista bastante estrecha de eventos que (des)permitirán: https://docs.python.org/3/library/audit_events.html

Estaría agradecido a cualquiera que me señalara los defectos de este enfoque.


EDITAR: ¡Así que esto tampoco es seguro ! Estoy muy agradecido con @Emu por su ingenioso truco mediante la captura de excepciones y la introspección:

#!/usr/bin/python3.8
from sys import addaudithook
def block_mischief(event,arg):
    if 'WRITE_LOCK' in globals() and ((event=='open' and arg[1]!='r') or event.split('.')[0] in ['subprocess', 'os', 'shutil', 'winreg']):
        raise IOError('file write forbidden')

addaudithook(block_mischief)
WRITE_LOCK = True
exec("""
import sys
def r(a, b):
    try:
        raise Exception()
    except:
        del sys.exc_info()[2].tb_frame.f_back.f_globals['WRITE_LOCK']
import sys
w = type('evil',(object,),{'__ne__':r})()
sys.audit('open', None, w)
open('/tmp/FILE','w').write('pwned by l33t h4xx0rz')""", dict(locals()))

Supongo que la auditoría + subprocesamiento es el camino a seguir, pero no lo use en máquinas de producción:

https://bitbucket.org/fdominec/experimental_sandbox_in_cpython38/src/master/sandbox_experiment.py

dominecf avatar Jun 30 '2020 15:06 dominecf

AFAIK, es posible ejecutar un código en un entorno completamente aislado:

exec somePythonCode in {'__builtins__': {}}, {}

Pero en ese entorno no puedes hacer casi nada :) (ni siquiera puedes importun módulo; pero aun así un usuario malintencionado puede ejecutar una recursividad infinita o provocar que te quedes sin memoria). Probablemente quieras agregar algunos módulos que serán la interfaz a tu motor de juego.

Messa avatar Jun 18 '2010 08:06 Messa