在python Web服务器上执行数学用户代码,最简单的安全方法是什么?

时间:2012-05-18 05:37:14

标签: python security web.py

我之前已经意识到这个问题,但是这种情况略有不同。

我想运行一个python图像板(使用web.py),这将允许用户通过提交代码生成新图像。代码将采用单个函数的形式,它采用像素的x,y坐标并返回r,g,b值,例如:

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

只需要非常小的语法,并且它不一定必须是python。使用范围有限的exec似乎太不安全了,使用PyPy或VM似乎不必要地复杂(我对这一切都很新)。

有没有一种以更小的语言执行代码的pythonic方法,而不是沙箱化?是python的一个子集(解析和白名单?),还是我可以嵌入的数学导向语言?

2 个答案:

答案 0 :(得分:3)

这是我采用的解决方案。有关此方法安全性的讨论,请参阅

感谢arifwn,我开始探索Python的ast(抽象语法树)模块。该模块提供了一个用于遍历树的类ast.NodeVisitor。此代码将NodeVisitor子类化,以创建语法检查程序,将基本数学所需的代码列入白名单。函数调用和名称是专门监视的,因为只允许某些函数,并且只允许使用未使用的名称。

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

请注意,这只是为了接受代码的数学部分,提供了函数定义和return语句。

这种将所有必需的安全构造列入白名单的方法,特别是白名单所需的不安全构造,可以被修改以产生许多有用的Python子集;非常适合用户脚本!

请注意,为了安全地执行此操作,它应该在其自己的线程中具有超时,以减少名称冲突,并在用户代码生成无限循环或类似情况时超时。

答案 1 :(得分:1)

有很多很棒的信息on the pysandbox pypi page