限制 Python 的语法以安全地执行用户代码。这是一种安全的方法吗?

Posted

技术标签:

【中文标题】限制 Python 的语法以安全地执行用户代码。这是一种安全的方法吗?【英文标题】:Restricting Python's syntax to execute user code safely. Is this a safe approach? 【发布时间】:2012-05-26 12:38:31 【问题描述】:

原问题:

Executing mathematical user code on a python web server, what is the simplest secure way?

我希望能够在 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

这似乎接受了所需的语法,但我对编程相当陌生,可能会遗漏许多巨大的安全漏洞。

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

【问题讨论】:

对我来说,我看起来很安全......但请注意:脚本中的名称有些泄漏到沙箱中。如果我测试 x 它说 x 是保留名称 但如果我测试 y 它说 name 'y' not defined 网络服务器有什么样的安全机制来运行python脚本? @rodrigo:没错!在部署中,我希望它在自己的线程中运行,以将其与其他名称隔离。 @Joel:没有其他安全措施,这是一个用 web.py 编写的非常基础的项目(因此我想要一个简单的 Python 解决方案来安全地运行脚本) 啊,抱歉线程会超时。 【参考方案1】:

Openerp 的源代码包含一个safe_eval.py,它做类似的事情。但它不是检查源代码的 ast,而是限制允许执行的字节码。我想你也可以看看它:)

【讨论】:

【参考方案2】:

你看过pypy's sandboxing features吗?据说它比任何 CPython 沙盒工作都安全得多。您甚至可以限制堆大小和 cpu 执行时间以防止拒绝服务。

【讨论】:

谢谢,请参阅the original question。在这种情况下,我正在寻求一个简单的、pythonic 的解决方案,而不是最好的安全性。 啊啊,我现在明白了。我确实查看了原始问题,但只检查了答案——看起来有人在 cmets 中推荐了 PyPy。好吧,只要你知道 PyPy 选项可用,祝你白名单方法好运!【参考方案3】:

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

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

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

编辑:

最后,我还建议更新您的 Python 版本以防止出现"hashDoS" attack。

【讨论】:

谢谢,很高兴您只能看到两个!当代码被检查并执行时,它将在它自己的线程中超时。不过我对跨站点脚本一无所知,我会继续阅读的 我尝试查找此内容,但没有找到太多内容。 python 哈希冲突是一个现实的利用吗? 啊,是的,跨站点脚本是一个漏洞 - 将修复 不过,我还是会转义输出。我见过非常聪明的代码片段设法规避了所做的假设。这很危险,任何人都会告诉你同样的事情:不要对攻击者能做什么或不能做什么做出假设——总是走安全的路线。比抱歉更安全:) @emboss 新手问题:通过函数检查器运行方程字符串后,实际求解方程的最佳方法是什么?是否对字符串进行了足够的清理以仅在其上使用 eval 还是有更安全的方法?

以上是关于限制 Python 的语法以安全地执行用户代码。这是一种安全的方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在我的移动网站上安全地限制桌面用户的访问吗? [复制]

将 Ansible 剧本安全地限制在单台机器上?

安全沙箱并执行用户提交的 JavaScript?

从Web浏览器执行命令提示符(安全地)

尝试更新 python2.7 程序以适应 python3.3 新语法限制

Web安全相关:SQL注入(SQL Injection)