【发布时间】:2012-09-21 02:34:47
【问题描述】:
好的。我knowtheexpertshavespoken,你不应该永远在不受信任的数据上使用python的eval(),永远。我并不比世界其他人聪明,甚至不应该尝试这个。但!不管怎样,我要去。
我的基本问题是,我希望使用 python 语法的子集编写一个小型计算器评估程序,该程序将接受不受信任的输入。我知道:使用ply 或pyparsing 并编写一个解析器就可以了。将全局变量和局部变量传递给 eval() 是不行的。
我见过的所有方法(并且一直持怀疑态度)都试图列举邪恶。在这里,我试图枚举 good —— 获取一个 AST,只允许少数节点类型,然后验证任何调用都是针对一组白名单函数中的一个。这是一个小实现(和a gist):
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__ 或任何东西。
我...我认为这行得通。我疯了吗?
【问题讨论】:
-
这只是另一个例子,因为我实际上是同一个 Nate Vack :)
标签: python security parsing eval