8

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

我想运行一个 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 似乎不必要地复杂(我对这一切都很陌生)。

与其对它进行沙箱化,是否有一种 Python 的方式以更小的语言执行代码?是python的一个子集(解析和白名单?),还是我可以嵌入的面向数学的语言?

4

2 回答 2

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

请注意,这旨在仅接受代码的数学部分,提供函数定义和返回语句。

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

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

于 2012-05-18T14:15:43.657 回答
1

pysandbox pypi 页面上有很多很棒的信息。

于 2012-05-18T06:10:37.327 回答