2012-05-18 17 views
7

Mi rendo conto che questa domanda è stata fatta prima, tuttavia questo caso è leggermente diverso.Eseguendo il codice utente matematico su un server web Python, qual è il modo più semplice e sicuro?

Voglio eseguire una python imageboard (utilizzando web.py), che consentirà agli utenti di generare nuove immagini inviando il codice. Il codice sarà di forma di una singola funzione che prende le coordinate x, y di un pixel e restituisce la r, g, valori di b, ad esempio:

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

Solo è necessaria una piccola sintassi, e non deve essere necessariamente pitone. L'uso di exec con un ambito limitato sembra essere troppo insicuro, e l'uso di PyPy o di una VM sembra inutilmente complesso (sono abbastanza nuovo per tutto questo).

Piuttosto che sandboxing, c'è un modo pietoso per eseguire il codice in un linguaggio molto più piccolo? O un sottoinsieme di python (parsing e whitelisting?) O un linguaggio matematico che posso incorporare?

+2

In realtà avrei usato una sandbox PyPy. –

+0

Altre risposte che ho letto hanno votato contro ... Quindi non ho davvero guardato in PyPy - lo controllerò grazie – SudoNhim

+0

Ottima domanda, forse PyPy è la risposta. Stavo parlando oggi di come Python potrebbe essere un po 'corto qui, rispetto a dire lua. –

risposta

2

Questa è la soluzione con cui sono andato. Per una discussione sulla sicurezza di questo approccio, vedere

Grazie a arifwn, sono entrato in esplorare modulo di Python ast (abstract albero di sintassi). Questo modulo fornisce una classe ast.NodeVisitor per attraversare l'albero. Questo codice sottoclasse NodeVisitor per creare un verificatore di sintassi che autorizzi il codice necessario per la matematica di base. Le chiamate e i nomi delle funzioni sono monitorati in modo specifico, poiché dovrebbero essere consentite solo alcune funzioni e dovrebbero essere consentiti solo nomi non utilizzati.

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 

Si noti che questo è progettato per accettare solo la parte matematica del codice, la definizione della funzione e la dichiarazione di ritorno sono forniti.

Questo metodo di whitelisting tutti i costrutti sicuri necessari e in particolare la whitelisting richiedeva costrutti non sicuri, potrebbe essere modificato per produrre molti sottoinsiemi utili di Python; eccellente per gli script utente!

Si noti che per eseguire questo in modo sicuro, dovrebbe essere nella propria thread con un timeout, per ridurre il numero di collisioni e timeout se il codice utente genera un ciclo infinito o simile.

+0

Questa dovrebbe essere una domanda indipendente IMO. – TryPyPy

+0

Siamo spiacenti. Conosco qualcuno che si spera possa controllarlo per me; se lo trova OK lo riformatterò per essere più simile a una risposta. (Altrimenti lo cancellerò). – SudoNhim

+0

Quello che intendevo era: avrai più occhi (e probabilmente nuovi suggerimenti) se convertirai questa risposta in una nuova domanda :) – TryPyPy

Problemi correlati