返回介绍

建议14:警惕 eval() 的安全漏洞

发布于 2024-01-30 22:19:09 字数 3650 浏览 0 评论 0 收藏 0

如果你了解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存在什么样的安全漏洞呢?来看一个简单的例子:

import sys
from math import *
def ExpCalcBot(string):
  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"),会有什么样的输出呢?你会惊讶地发现它会显示当前目录下的所有文件列表,输出如下:

C:\test>python tests.py
Hi,I am ExpCalcBot. please input your experssion or enter e to end
Please enter a number or operation. Enter c to complete. :
__import__("os").system("dir")
Your answer is 
驱动器 C 
中的卷是 SYSTEM
卷的序列号是 A0E0-1A46
 C:\test 
的目录
2013/07/31  12:28  <DIR>      .
2013/07/31  12:28  <DIR>      ..
2012/07/24  08:11       859,919 2012-07-24-010.jpg
2012/07/24  08:11     1,037,015 2012-07-24-011.jpg
2012/07/24  08:19     1,242,667 2012-07-24-012.jpg
2013/07/31  12:26         0 test.txt
2013/07/31  12:27         503 tests.py
         5 
个文件    3,140,104 
字节
         2 
个目录 422,254,702,592 
可用字节
 0
Please enter a number or operation. Enter c to complete. :

于是顿时,有人的“坏心眼”来了,他输入了如下字符串,可悲的事情发生了,当前目录下的所有文件都被删除了,包括test.py,而这一切没有任何提示,悄无声息。

__import__("os").system("del * /Q")  
!!!不要轻易在你的计算机上尝试
Your answer is 0

试想,在网络环境下这是不是很危险?也许你会辩护,那是因为你没有在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。上面的例子请读者使用literal_eval自行试验,你会体会得更加深刻。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文