Python内置函数eval()用来对表达式进行求值:
>>> eval('3+5')
8
>>> a = 3
>>> b = 5
>>> eval('a+b')
8
这个函数在Python 3.x中使用较多,因为在Python 3.x中使用input()函数接收用户输入时一律返回字符串,经常需要进行类型转换,这时候常使用eval()函数,例如:
>>> x = input('Please input:')
Please input:35
>>> x
'35'
>>> type(x)
<class 'str'>
>>> eval(x)
35
>>> type(_)
<class 'int'>
>>> x = input('Please input:')
Please input:[1, 2, 3]
>>> x
'[1, 2, 3]'
>>> type(x)
<class 'str'>
>>> eval(x)
[1, 2, 3]
>>> type(_)
<class 'list'>
但是,需要注意的是,我们无法保证用户总是输入合法的数据,而恶意黑客也是利用一些程序的bug来精心构造非法输入来触发漏洞,从而造成破坏和攻击。
>>> eval("__import__('os').startfile('notepad.exe')") #启动记事本程序
>>> eval('__import__("os").system("dir")') #列出当前目录的文件列表
>>> eval("__import__('os').system('md testtest')") #在当前目录中创建子目录testtest
当然,这里只是一些善意的演示,通过精心构造输入,充分利用Python标准库和扩展库的功能,会实现很多善意或恶意的目的。
如果程序中确实需要用户输入,并且确实需要对用户的输入进行eval(),那么为了保证安全,可以对用户输入进行敏感字符检查和过滤,例如:
>>> x = input('Please input:')
Please input:__import__("os").system("dir")
>>> if '__import__(' in x: #使用关键字in进行检查
print('error')
error
>>> x = x.replace('__import__(', '***') #使用字符串方法replace()进行过滤和替换
>>> x
'***"os").system("dir")'
>>> eval(x) #如果用户输入中有敏感字符,过滤后会引发异常
Traceback (most recent call last):
File "<pyshell#24>", line 1, in <module>
eval(x)
File "<string>", line 1
***"os").system("dir")
^
SyntaxError: invalid syntax
或者,也可以采用这样的办法,在程序第一行增加一行eval = print,这样运行程序时会显示用户输入而不是进行求值计算,当反复测试发现用户输入不会带来危害后再删除这一行。
还有一个办法是使用Python标准库ast提供的literal_eval()方法,功能与eval()一致,但更安全,例如:
>>> import ast
>>> ast.literal_eval('3+5') #正常执行
8
>>> ast.literal_eval("__import__('os').startfile('notepad.exe')") #引发异常
Traceback (most recent call last):
File "<pyshell#31>", line 1, in <module>
ast.literal_eval("__import__('os').startfile('notepad.exe')")
File "C:\Python35\lib\ast.py", line 84, in literal_eval
return _convert(node_or_string)
File "C:\Python35\lib\ast.py", line 83, in _convert
raise ValueError('malformed node or string: ' + repr(node))
ValueError: malformed node or string: <_ast.Call object at 0x0000000003539588>