eval()函数是Python的内置函数,功能非常强大,但是存在不小的安全隐患。有些企业或项目出于安全考虑,禁止使用eval()函数,会在一些安全相关的扫描校验中进行识别和拦截,杜绝使用。
究竟eval()函数强大在哪?又有什么安全隐患?本文将逐一进行总结分析。
eval()函数介绍
eval()函数语法:
eval(expression[, globals[, locals]])
- expression: 字符串表达式。
- globals: 可选参数,全局变量,如果设置,则必须是一个字典对象。
- locals: 可选参数,局部变量,如果设置,则可以是任何映射(mapping)对象。如果只设置了globals,locals默认与globals一样。
eval()函数的作用是将字符串当成有效的表达式来求值并返回计算的结果。相当于去掉字符串首尾的引号,并执行去掉引号后的语句,返回执行的结果。
主要效果体现为:
- 执行一个字符串表达式,并返回表达式的值。
- 将字符串转成对应格式的数据对象(如int、list、tuple或dict)。
eval()函数的强大功能
1.执行字符串表达式并返回结果。
Output:
eval()可以对字符串中的数字加法进行计算,返回计算结果。也可以直接执行字符串中的列表推导式这类表达式,返回执行的结果。
2.执行表达式时支持将变量传到字符串中。
Output:
在eval()中执行表达式还支持传参,可以在当前 .py 代码环境中定义变量,也可以通过eval()函数的globals参数和locals参数传值。优先级locals高于globals,globals高于当前 .py代码环境的变量。
3.返回对应类型的数据。
Output:
eval()函数直接返回字符串内容对应的数据类型,作用相当于将字符串首尾的引号去掉,如果不用eval(),自己转换数据类型,需要好几个步骤。
eval()函数经常和input()函数配合使用,直接将用户输入的字符串转换成对应类型的数据。
eval()函数也经常用于从配置文件中读取内容,读取内容的同时直接转换成对应类型。
eval()函数的安全隐患
eval()函数功能非常强大,但同时也存在不小的安全隐患,原因正是eval()可以将字符串转成表达式执行。
Output:
如果在执行eval()函数的运行环境中导入了os模块,恶意用户可以通过eval()函数调用os模块中的系统命令函数system(),执行一系列的系统命令来达到他的目的。
如os.system(‘whoami’)可以查看当前系统的登录用户、os.system(‘dir’)可以查看当前目录下的所有文件。假如执行的是查看源码或删除数据等的命令,将会产生严重的后果。
针对这种隐患,有没有办法限制用户执行系统命令呢?
Output:
上面的代码运行环境中导入了os库,eval()中可以正常调用。假如将eval()中的globals和locals参数设置成空,eval()中就找不到os库了,执行代码报错。
eval()函数中变量加载的优先级顺序为:局部变量locals > 全局变量globals > 当前 .py环境中的变量。
这里需要注意,如果未设置locals或locals为空,则locals与globals一样。假如locals中不存在值,会再到globals中寻找值。因此,要设置locals和globals中都没有os库,才能避免用户调用。(实际应用时并不一定都是将locals和globals设置为空,设置为空只是一种示例)。
通过对locals和globals的限制,避免了用户调用当前运行环境中导入的os库。但是,限制用户使用已导入的库,用户可以自己导入库并使用。
Output:
如果恶意用户发现当前的运行环境中没有导入os,或者导入的os库被限制使用,调用os.system()报错。恶意用户会尝试自己导包,用__import__(‘os’)可以在eval()函数中导入os库,同时执行一系列的系统命令来达到他的目的。
这种方法是在每次执行时都导包,并立即链式执行系统命令,在globals和locals中去掉os并不能起到限制。而且,os库是python中的标准库,只要有python就一定有os库,用户必然能导入成功。
那针对这种隐患,有没有办法不让用户导包呢?
Output:
用户自己在eval()函数中导包,是使用__import__()函数实现的。_import_()函数是python的内建函数,是用于动态导库的函数。
Output:
在python中,内建函数都在__builtins__模块中,在启动python时,python解释器就直接导入了__builtins__模块中的函数。_import_()函数就是__builtins__模块中的一员,也就是说,python解释器默认导入了__import__()函数,用户可以直接调用。
上面的代码在globals参数中将__builtins__设置成None,eval()函数就获取不到_import_()函数了,无法自己导包,执行代码报错。
但是,限制用户导包,恶意用户还可以通过其他途径获取到os库。
Output:
上面这种方式也可以成功执行系统命令。代码中利用__class__和__subclasses__动态加载了基类object的所有子类(可以执行下面这行代码查看当前环境中基类都有哪些子类),然后找到了zipimporter,用zipimporter动态加载setuptools库的 .egg包,再链式调用load_module()成功导入setuptools库,从而成功调用os库执行系统命令。
在python中,有一些库中内置了os库,导入这些库后就能调用os库,其中就包含setuptools,此外还有configobj、urllib、urllib2等。
当然,执行上面的代码需要有对应的.egg包,如果你也想演示看效果,你可以先在自己的电脑磁盘中全局搜一下,找不到再到网络下载。
以上是eval()函数的一些安全隐患,可谓是防不胜防,在写代码时无形中就需要和恶意用户进行很多回合的思维对抗,假如有哪个细节稍微考虑不周,就会留下很大的隐患。而且,关于eval(),恶意用户还有很多可以利用的方法,如删数据、暴力占满服务器的CPU资源等。
既然用了就防不胜防,那只有不用才不会留下隐患,所以,在一些企业和项目中就禁用了eval()函数。
(当然,python中也有替代方案,那就是ast.literal_eval()函数,ast.literal_eval()函数会判断字符串内容去掉首尾的引号后是不是合法的python类型,如果不是就报错,因此ast.literal_eval()函数也只能进行类型转换。)
以上就是本文的全部内容,希望能对你有帮助,欢迎点赞、收藏、评论和关注。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
机房租用,北京机房租用,IDC机房托管, http://www.e1idc.net