2012-05-18 6 views
7

Me doy cuenta de que esta pregunta se ha hecho antes, sin embargo, este caso es ligeramente diferente.Ejecutando el código de usuario matemático en un servidor web python, ¿cuál es la forma más segura?

Quiero ejecutar una imagen de python (usando web.py), que permitirá a los usuarios generar nuevas imágenes mediante el envío de código. El código será de la forma de una sola función que toma las coordenadas x, y de un píxel y devuelve el r, g, los valores de B, por ejemplo:

def simpleGradient(xrel,yrel): 
    r = xrel*256 
    g = yrel*256 
    b = 0 
    return [r,g,b] 

Sólo se requiere una muy pequeña sintaxis, y no necesariamente tiene que ser python. Usar exec con alcance limitado parece ser demasiado inseguro, y usar PyPy o una VM parece innecesariamente complejo (soy bastante nuevo en todo esto).

En lugar de guardarlo en un sandbox, ¿hay alguna forma pitón de ejecutar el código en un lenguaje mucho más pequeño? ¿O un subconjunto de Python (análisis sintáctico y lista blanca?), O un lenguaje orientado a las matemáticas que puedo incrustar?

+2

En realidad usaría un sandbox PyPy. –

+0

Varias otras respuestas que leí votaron en contra ... Así que realmente no he investigado PyPy. Lo comprobaré gracias – SudoNhim

+0

Una gran pregunta, quizás PyPy es la respuesta. Estaba hablando hoy sobre cómo Python podría ser un poco 'corto aquí, en comparación con decir lua. –

Respuesta

2

Esta es la solución que fui. Para una discusión sobre la seguridad de este enfoque, ver

Gracias a arifwn, llegué a explorar ast módulo de Python (árbol de sintaxis abstracta). Este módulo proporciona una clase ast.NodeVisitor para atravesar el árbol. Este código crea subclases NodeVisitor para crear un corrector de sintaxis que incluye el código necesario para matemáticas básicas. Las llamadas a funciones y los nombres se supervisan especialmente, ya que solo se deben permitir ciertas funciones y solo deben permitirse los nombres no utilizados.

import ast 

allowed_functions = set([ 
    #math library 
    'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 
    'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 
    'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 
    'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp', 
    'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 
    'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc', 
    #builtins 
    'abs', 'max', 'min', 'range', 'xrange' 
    ]) 

allowed_node_types = set([ 
    #Meta 
    'Module', 'Assign', 'Expr', 
    #Control 
    'For', 'If', 'Else', 
    #Data 
    'Store', 'Load', 'AugAssign', 'Subscript', 
    #Datatypes 
    'Num', 'Tuple', 'List', 
    #Operations 
    'BinOp', 'Add', 'Sub', 'Mult', 'Div', 'Mod', 'Compare' 
    ]) 

safe_names = set([ 
    'True', 'False', 'None' 
    ]) 


class SyntaxChecker(ast.NodeVisitor): 

    def check(self, syntax): 
     tree = ast.parse(syntax) 
     self.passed=True 
     self.visit(tree) 

    def visit_Call(self, node): 
     if node.func.id not in allowed_functions: 
      raise SyntaxError("%s is not an allowed function!"%node.func.id) 
     else: 
      ast.NodeVisitor.generic_visit(self, node) 

    def visit_Name(self, node): 
     try: 
      eval(node.id) 
     except NameError: 
      ast.NodeVisitor.generic_visit(self, node) 
     else: 
      if node.id not in safe_names and node.id not in allowed_functions: 
       raise SyntaxError("%s is a reserved name!"%node.id) 
      else: 
       ast.NodeVisitor.generic_visit(self, node) 

    def generic_visit(self, node): 
     if type(node).__name__ not in allowed_node_types: 
      raise SyntaxError("%s is not allowed!"%type(node).__name__) 
     else: 
      ast.NodeVisitor.generic_visit(self, node) 

if __name__ == '__main__': 
    x = SyntaxChecker() 
    while True: 
     try: 
      x.check(raw_input()) 
     except Exception as e: 
      print e 

Tenga en cuenta que esto está diseñado para aceptar sólo la parte matemática del código, se proporcionan la definición de la función y la instrucción de retorno.

Este método de incluir en una lista blanca todas las construcciones seguras requeridas y específicamente las listas blancas requeridas construcciones inseguras, podría modificarse para producir muchos subconjuntos útiles de Python; ¡Excelente para guiones de usuario!

Tenga en cuenta que para que esto se ejecute de forma segura, debe estar en su propio hilo con un tiempo de espera, para reducir colisiones de nombres y tiempo de espera si el código de usuario genera un bucle infinito o similar.

+0

Esto debería ser una pregunta independiente IMO. – TryPyPy

+0

Lo siento. Conozco a alguien que con suerte puede verificarlo por mí; si lo encuentra bien lo formatearé para que sea más como una respuesta. (De lo contrario, lo eliminaré). – SudoNhim

+0

Lo que quise decir es: obtendrás más ojos (y probablemente nuevas sugerencias) si conviertes esta respuesta en una nueva pregunta :) – TryPyPy

Cuestiones relacionadas