Python 警惕eval()的安全漏洞,如果你了解JavaScript或者PHP等,那么你一定对eval()所有了解。如果你并没有接触过也没关系,eval()函数的使用非常简单。
>>> eval("1+1==2") #进行判断
True
>>>
>>> eval("'A'+'B'") #字符连接
'AB'
>>> eval("1+2") #数字相加
3
Python中eval()函数将字符串str当成有效的表达式来求值并返回计算结果。其函数声明如下:
eval(expression[, globals[, locals]])
其中,参数globals为字典形式,locals为任何映射对象,它们分别表示全局和局部命名空间。如果传入globals参数的字典中缺少builtins的时候,当前的全局命名空间将作为globals参数输入并且在表达式计算之前被解析。locals参数默认与globals相同,如果两者都省略的话,表达式将在eval()调用的环境中执行。
“eval is evil”
(eval是邪恶的),这是一句广为人知的对eval的评价,它主要针对的是eval()的安全性。那么eval存在什么样的安全漏洞呢?来看一个简单的例子:
from math import *
def ExpCalcBot(user_func):
try:
print 'Your answer is',eval(user_func) #计算输入的值
except NameError:
print "The expression you enter is not valid"
print 'Hi,I am ExpCalcBot. please input your experssion or enter e to end'
inputstr =''
while 1:
print 'Please enter a number or operation. Enter c to complete. :'
inputstr = raw_input()
if inputstr == str('e') : #遇到输入为e的时候退出
sys.exit()
elif repr(inputstr) != repr(''):
ExpCalcBot(inputstr)
inputstr = ''
上面这段代码的主要功能是:根据用户的输入,计算Python表达式的值。它有什么问题呢?如果用户都是素质良好,没有不良目的的话,那么这段程序也许可以满足基本需求。比如,输入1+sin(20)
会输出结果1.91294525073。但如果它是一个Web页面的后台调用(当然,你需要做一定的修改),由于网络环境下运行它的用户并非都是可信任的,问题就出现了。因为eval()可以将任何字符串当做表达式求值,这也就意味着有空子可钻。上面的例子中假设用户输入import(“os”).system(“dir”),会有什么样的输出呢?你会惊讶地发现它会显示当前目录下的所有文件列表,输出如下:
于是顿时,有人的“坏心眼”来了,他输入了如下字符串,可悲的事情发生了,当前目录下的所有文件都被删除了,包括coolcou.py,而这一切没有任何提示,悄无声息。
__import__("os").system("del * /Q")
!!!不要轻易在你的计算机上尝试
试想,在网络环境下这是不是很危险?也许你会辩护,那是因为你没有在globals参数中禁止全局命名空间的访问。好,我们按照你说的来试验一下:将函数ExpCalcBot修改一下,其中math_fun_list限定为几个常用的数学函数。修改后的函数如下:
def ExpCalcBot(string):
try:
math_fun_list = ['acos', 'asin', 'atan', 'cos', 'e','log', 'log10','pi',
'pow', 'sin', 'sqrt', 'tan']
math_fun_dict = dict([ (k, globals().get(k)) for k in math_fun_list ])
#形成可以访问的函数的字典
print 'Your answer is',eval(string,{"__builtins__": None},math_fun_dict)
except NameError:
print "The expression you enter is not valid"
再次运行程序(请读者自行试验)你会惊喜地发现上面的命令被看着无效表达式,你的辩护是对的,这确实是我们想要的。很好,安全问题不再是个问题!但仔细想想真是这样的吗?试试输入以下字符:
[c for c in ().__class__.__bases__[0].__subclasses__() if c.__name__ =='Quitter'][0](0)()
# ().__class__.__bases__[0].__subclasses__()
用来显示object类的所有子类。类Quitter与”quit”功能绑定,因此上面的输入会直接导致程序退出。
注:你可以在Python的安装目录下的
Lib\site.py
中找到其类的定义。读者也可以自行在Python解释器中输入print().__class__.__bases__[0].__subclasses__()
看看输出结果是什么。
因此对于有经验的侵入者来说,他可能会有一系列强大的手段,使得eval可以解释和调用这些方法,从而带来更大的破坏。此外,eval()函数也给程序的调试带来一定困难,要查看eval()里面表达式具体的执行过程很难。因此在实际应用过程中如果使用对象不是信任源,应该尽量避免使用eval,在需要使用eval的地方可用安全性更好的ast.literal_eval
替代。literal_eval函数具体详情可以参考文档http://docs.python.org/2/library/ast.html#ast.literal_eval
。
酷客网相关文章:
评论前必须登录!
注册