使用 ast 和白名单来确保 python 的 eval() 安全?



好。我知道专家们已经说过了,你永远不应该在不可信的数据上使用python的eval()。我并不比世界上其他人聪明,甚至不应该尝试这个。但是我无论如何都要去。

我的基本问题是,我希望编写一个小型计算器评估程序,使用python语法的子集,接受不受信任的输入。我知道:使用ply或pyparsing并编写一个解析器,我们就这样开始了。把全局和局部传递给eval()是行不通的。

我所见过(也一直对此心存疑虑)的所有方法都试图列举邪恶。在这里,我试图枚举good——获取一个AST,只允许几个节点类型,然后验证任何调用都是对一组白名单函数之一的调用。以下是一个小型实现(以及要点):

import ast
import math
SAFE_FX = {
'exp': math.exp,
}
SAFE_NODES = set(
(ast.Expression,
ast.Num,
ast.Call,
ast.Name,
ast.Load,
ast.BinOp,
ast.Add,
ast.Sub,
ast.Mult,
ast.Div,)
)
class CleansingNodeVisitor(ast.NodeVisitor):
def generic_visit(self, node):
if type(node) not in SAFE_NODES:
raise Exception("%s not in SAFE_NODES" % type(node))
super(CleansingNodeVisitor, self).generic_visit(node)
def visit_Call(self, call):
if call.func.id not in SAFE_FX:
raise Exception("Unknown function: %s" % call.func.id)
def my_safe_eval(s):
tree = ast.parse(s, mode='eval')
cnv = CleansingNodeVisitor()
cnv.visit(tree)
compiled = compile(tree, s, "eval")
return(eval(compiled, SAFE_FX))

因此,my_safe_eval('2*(4+exp(1.3))')起作用,而my_safe_eval('[].__class__')起作用,my_safe_eval('open("/something/evil")')同样被禁止——而不禁止__builtins____locals__或任何东西。

我。。。我认为这是有效的。我疯了吗?

Zope有一个名为RestrictedPython的东西,您可能需要检查它,至少是为了验证您的方法或可能重用他们的代码。它是可配置和可重复使用的。

这是我对类似问题的另一个回答。

试试asteval,这似乎是你需要的东西。否则就有这个安全的评估

最新更新