美文网首页Python小哥哥Python小推车初学者
Python 的 eval() 与 exec() 安全用法最佳实

Python 的 eval() 与 exec() 安全用法最佳实

作者: 我爱学python | 来源:发表于2019-04-25 14:58 被阅读5次

    Python 提供了很多内置的工具函数(Built-in Functions),今天我们一起来探索两个函数的安全用法:evel() 与 exec()。

    1、eval 的基本用法 

    由此可见,当指定了命名空间的时候,变量会在对应命名空间中查找。而且,它们的值不会覆盖实际命名空间中的值。 

    2、exec 的基本用法 

    3、一些细节辨析 

    两个函数都很强大,它们将字符串内容当做有效的代码执行。这是一种字符串驱动的事件 ,意义重大。然而,在实际使用过程中,存在很多微小的细节,此处就列出我所知道的几点吧。 

    常见用途:将字符串转成相应的对象,例如 string 转成 list ,string 转成 dict,string 转 tuple 等等。 

    eval() 函数的返回值是其 expression 的执行结果,在某些情况下,它会是 None,例如当该表达式是 print() 语句,或者是列表的 append() 操作时,这类操作的结果是 None,因此 eval() 的返回值也会是 None。 

    exec() 函数的返回值只会是 None,与执行语句的结果无关,所以,将 exec() 函数赋值出去,就没有任何必要。所执行的语句中,如果包含 return 或 yield ,它们产生的值也无法在 exec 函数的外部起作用。 

    两个函数中的 globals 和 locals 参数,起到的是白名单的作用,通过限定命名空间的范围,防止作用域内的数据被滥用。 

    conpile() 函数编译后的 code 对象,可作为 eval 和 exec 的第一个参数。compile() 也是个神奇的函数,我翻译的上一篇文章《Python骚操作:动态定义函数》就演示了一个动态定义函数的操作。 

    吊诡的局部命名空间:前面讲到了 exec() 函数内的变量是可以改变原有命名空间的,然而也有例外。 

    按照前面的理解,预期的结果是局部变量中会存入变量 y,因此两次的打印结果都会是 2,然而实际上的结果却是: 

    KeyError 指的是在字典中不存在对应的 key 。本例中 y 作了声明,却因为循环引用而无法完成赋值,即 key 值对应的 value 是个无效值,因此读取不到,就报错了。 

    此例还有 4 个变种,我想用一套自恰的说法来解释它们,但尝试了很久,未果。留个后话吧,等我想明白,再单独写一篇文章。 

    4、为什么要慎用 eval() ? 

    很多动态的编程语言中都会有 eval() 函数,作用大同小异,但是,无一例外,人们会告诉你说,避免使用它。 

    为什么要慎用 eval() 呢?主要出于安全考虑,对于不可信的数据源,eval 函数很可能会招来代码注入的问题。 

    在以上例子中,我的隐私数据就被暴露了。而更可怕的是,如果将命令改为rm -rf ~ ,那当前目录的所有文件都会被删除干净。 

    针对以上例子,有一个限制的办法,即指定 globals 为 {'__builtins__': None} 或者 {'__builtins__': {}} 。 

    __builtins__ 包含了内置命名空间中的名称,在控制台中输入 dir(__builtins__) ,就能发现很多内置函数、异常和其它属性的名称。在默认情况下,eval 函数的 globals 参数会隐式地携带__builtins__ ,即使是令 globals 参数为 {} 也如此,所以如果想要禁用它,就得显式地指定它的值。 

    上例将它映射成 None,就意味着限定了 eval 可用的内置命名空间为 None,从而限制了表达式调用内置模块或属性的能力。 

    但是,这个办法还不是万无一失的,因为仍有手段可以发起攻击。 

    某位漏洞挖掘高手在他的博客中分享了一个思路,令人大开眼界。其核心的代码是下面这句,你可以试试执行,看看输出的是什么内容。 

    这行代码会导致 Python 直接 crash 掉。

    除了黑客的手段,简单的内容也能发起攻击。像下例这样的写法, 将在短时间内耗尽服务器的计算资源。 

    >>> eval("2 ** 888888888", {"__builtins__":None}, {})

    如上所述,我们直观地展示了 eval() 函数的危害性,然而,即使是 Python 高手们小心谨慎地使用,也不能保证不出错。 

    在官方的 dumbdbm 模块中,曾经(2014年)发现一个安全漏洞,攻击者通过伪造数据库文件,可以在调用 eval() 时发起攻击。(详情:https://bugs.python.org/issue22885) 

    无独有偶,在上个月(2019.02),有核心开发者针对 Python 3.8 也提出了一个安全问题,提议不在 logging.config 中使用 eval() 函数,目前该问题还是 open 状态。(详情:https://bugs.python.org/issue36022) 

    如此种种,足以说明为什么要慎用 eval() 了。同理可证,exec() 函数也得谨慎使用。 

    5、安全的替代用法 

    相关文章

      网友评论

        本文标题:Python 的 eval() 与 exec() 安全用法最佳实

        本文链接:https://www.haomeiwen.com/subject/grhzgqtx.html