使用eval进行表达式求值有多安全?
我正在建立一个网站,用户需要根据数据库表中的值来计算一些表达式。为了实现这个功能,我不想使用像pyparsing这样的工具,而是打算直接用Python。我找到了一种解决方案,基本上是用eval来计算表达式,并传递一个空的__builtins__
字典,这样用户就无法访问任何内置功能。同时,我还传递一个包含数据库值的局部字典。如果用户需要一些函数,我也可以把这些函数传过去,比如:
import datetime
def today():
return datetime.datetime.now()
expression = """ first_name.lower() == "anurag" and today().year == 2010 """
print eval(expression, {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})
所以我想问的是,这样做安全吗?我有三个考虑的点:
- 用户能否以某种方式访问我程序的当前状态或数据库表?
- 用户能否访问操作系统级别的调用?
- 用户能否通过循环或占用大量内存来使我的系统崩溃,比如执行range(10*8),在某些情况下,比如100**1000,这确实是个问题。不过我可以用tokenize来检查这样的操作,另外我会使用GAE,所以这也不是太大的担忧。
编辑:在我看来,这个问题并不是Q:661084的重复,因为前者讨论到的地方正是我想了解的内容。我想知道即使__builtins__
被阻止,用户是否还可以做一些坏事?
3 个回答
在程序中,你可以创建和调用任何定义的类,包括那些可以让Python解释器退出的类。此外,你还可以创建和执行任意的字节码字符串,这可能会导致解释器崩溃。想了解更多细节,可以查看Eval真的很危险。
当然,即使没有内置功能,也可以消耗掉所有可用内存或创建一个无限循环。有很多方法可以做到这一点,比如用'a'*999999*999999,或者创建一个无限循环:
>>> print eval('[[x.append(a) for a in x] for x in [[0]]]',
... {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})
至于第1)和第2),我不太确定,但看起来有点危险。我尝试过一种我认为可行的方法,但似乎有人已经考虑过这种攻击方式并把它封锁了:
>>> import datetime
>>> def today():
>>> return datetime.datetime.now()
>>>
>>> print eval('today.func_globals', {'__builtins__':{}}, {'first_name':'Anurag', 'today':today})
RuntimeError: restricted attribute
我本来期待得到这个:
{'__builtins__': <module '__builtin__' (built-in)>, ...
所以我觉得这可能不是个好主意。只要有一个小漏洞,就可能让别人访问到你的整个系统。你有没有考虑过其他不使用eval的方法?它们有什么问题吗?
使用 eval
是非常不安全的,即使你把内置的功能都清空和阻止了——攻击者可以从一个字面量开始,获取它的 __class__
等等,一直到 object
,然后再获取它的 __subclasses__
,依此类推……总之,Python 的自省功能太强大了,根本无法抵挡一个技术高超、决心坚定的攻击者。
ast.literal_eval
是安全的,如果你能接受它的一些限制……