Python 警惕eval()的安全漏洞

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”),会有什么样的输出呢?你会惊讶地发现它会显示当前目录下的所有文件列表,输出如下:
Python 警惕eval()的安全漏洞
于是顿时,有人的“坏心眼”来了,他输入了如下字符串,可悲的事情发生了,当前目录下的所有文件都被删除了,包括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 警惕eval()的安全漏洞

注:你可以在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

酷客网相关文章:

赞(0)

评论 抢沙发

评论前必须登录!