13

原始问题:

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

  • 我希望能够在 python 网络服务器上运行用户提交的代码。代码本质上将是简单和数学的。

由于需要这么小的 Python 子集,我目前的方法是通过遍历 Python 的抽象语法树来将允许的语法列入白名单。函数和名称得到特殊处理;只允许明确列入白名单的函数,并且只允许未使用的名称。

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.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

这似乎接受了所需的语法,但我对编程相当陌生,可能会遗漏任何数量的安全漏洞。

所以我的问题是:这是否安全,是否有更好的方法,还有我应该采取的其他预防措施吗?

4

3 回答 3

7

我注意到你仍然可以改进的两点:

您应该始终转义可以从某种形式的用户输入生成的任何输出。在您的示例中,不允许的标识符未经修改就被镜像回输出。这可能会被利用,例如跨站点脚本。因此,我还会转义任何此类错误消息以防止这种情况发生。

您需要注意的另一件事是拒绝服务攻击。想象一下,有人编写了一个 Ackermann 函数和一个脚本,将其提交到您的服务器数千次……为了防止这种情况,您应该对所提交的任何代码的执行时间进行时间限制。这是必不可少的,因为这种类型的“攻击”通常会在无意中发生——有人设法产生了无限循环。

编辑:

Finally, I would also recommend to update your Python version to prevent a "hashDoS" attack.

于 2012-05-19T00:23:18.080 回答
3

Have you looked at pypy's sandboxing features? It is reputedly much safer than any CPython sandboxing efforts. You can even limit the heap size and cpu execution time to prevent denial of service.

于 2012-05-20T03:05:01.307 回答
3

The Openerp's source code contains a safe_eval.py that do a similar thing. But Instead of checking the ast of the source, it restrict the byte code that is allowed to execute. I think you may also have a look on it :)

于 2012-05-20T05:37:49.557 回答