功能强大,但因安全隐患被企业禁用的Python内置函数

2023-10-29

功能强大,但却因安全隐患被企业禁用的Python内置函数

eval()函数是Python的内置函数,功能非常强大,但是存在不小的安全隐患。有些企业或项目出于安全考虑,禁止使用eval()函数,会在一些安全相关的扫描校验中进行识别和拦截,杜绝使用。

究竟eval()函数强大在哪?又有什么安全隐患?本文将逐一进行总结分析。

eval()函数介绍

eval()函数语法:

eval(expression[, globals[, locals]])

  • expression: 字符串表达式。
  • globals: 可选参数,全局变量,如果设置,则必须是一个字典对象。
  • locals: 可选参数,局部变量,如果设置,则可以是任何映射(mapping)对象。如果只设置了globals,locals默认与globals一样。

eval()函数的作用是将字符串当成有效的表达式来求值并返回计算的结果。相当于去掉字符串首尾的引号,并执行去掉引号后的语句,返回执行的结果。

主要效果体现为:

  • 执行一个字符串表达式,并返回表达式的值。
  • 将字符串转成对应格式的数据对象(如int、list、tuple或dict)。

eval()函数的强大功能

1.执行字符串表达式并返回结果。

# 计算表达式
s = eval("5 + 7")
print('s: ', s)
s1 = eval('[i for i in range(10)]')
print('s1: ', s1)

Output:

s:  12
s1:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

eval()可以对字符串中的数字加法进行计算,返回计算结果。也可以直接执行字符串中的列表推导式这类表达式,返回执行的结果。

2.执行表达式时支持将变量传到字符串中。

# 传入变量
x = 111
s2 = eval("123 + x")
print('s2: ', s2)
# 设置globals
y = 2222
s3 = eval("1234 + y", {"y": 1111})
print('s3: ', s3)
# 设置globals, locals
z = 22222
s4 = eval("12345 + z", {"z": 11111}, {"z": 33333})
print('s4: ', s4)

Output:

s2:  234
s3:  2345
s4:  45678

在eval()中执行表达式还支持传参,可以在当前 .py 代码环境中定义变量,也可以通过eval()函数的globals参数和locals参数传值。优先级locals高于globals,globals高于当前 .py代码环境的变量。

3.返回对应类型的数据。

# 将引号中的内容还原成对应类型的数据
sta = '12345'
print(type(eval(sta)), eval(sta))
stb = '[1, 2, 3, 4, 5, 6, 7]'
print(type(eval(stb)), eval(stb))
stc = '(2, 4, 6, 8, 10)'
print(type(eval(stc)), eval(stc))
std = '{"beijing": 1, "shanghai": 2, "guangzhou": 3, "shenzhen": 4}'
print(type(eval(std)), eval(std))

Output:

<class 'int'> 12345
<class 'list'> [1, 2, 3, 4, 5, 6, 7]
<class 'tuple'> (2, 4, 6, 8, 10)
<class 'dict'> {'beijing': 1, 'shanghai': 2, 'guangzhou': 3, 'shenzhen': 4}

eval()函数直接返回字符串内容对应的数据类型,作用相当于将字符串首尾的引号去掉,如果不用eval(),自己转换数据类型,需要好几个步骤。

eval()函数经常和input()函数配合使用,直接将用户输入的字符串转换成对应类型的数据。

eval()函数也经常用于从配置文件中读取内容,读取内容的同时直接转换成对应类型。

eval()函数的安全隐患

eval()函数功能非常强大,但同时也存在不小的安全隐患,原因正是eval()可以将字符串转成表达式执行。

# 调用库执行系统命令
import os

eval("os.system('whoami')")
eval("os.system('echo 123')")

Output:

desktop-xxx\xxx
123

如果在执行eval()函数的运行环境中导入了os模块,恶意用户可以通过eval()函数调用os模块中的系统命令函数system(),执行一系列的系统命令来达到他的目的。

如os.system(‘whoami’)可以查看当前系统的登录用户、os.system(‘dir’)可以查看当前目录下的所有文件。假如执行的是查看源码或删除数据等的命令,将会产生严重的后果。

针对这种隐患,有没有办法限制用户执行系统命令呢?

import os

print('os' in globals())
eval("os.system('whoami')")
print("*"*30)
# 将globals参数设置成空
eval("os.system('whoami')", {}, {})
eval("os.system('whoami')", {})

Output:

True
desktop-xxx\xxx
******************************
Traceback (most recent call last):
  File "C:/Users/xxx/Desktop/eval_demo.py", line 49, in <module>
    eval("os.system('whoami')", {}, {})
  File "<string>", line 1, in <module>
NameError: name 'os' is not defined

上面的代码运行环境中导入了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库。但是,限制用户使用已导入的库,用户可以自己导入库并使用。

# 导入os库并执行系统命令
eval("__import__('os').system('whoami')")
eval("__import__('os').system('echo 123')")
# 增加globals和locals的限制
eval("__import__('os').system('whoami')", {})
eval("__import__('os').system('echo 123')", {}, {})

Output:

desktop-xxx\xxx
123
desktop-xxx\xxx
123

如果恶意用户发现当前的运行环境中没有导入os,或者导入的os库被限制使用,调用os.system()报错。恶意用户会尝试自己导包,用__import__(‘os’)可以在eval()函数中导入os库,同时执行一系列的系统命令来达到他的目的。

这种方法是在每次执行时都导包,并立即链式执行系统命令,在globals和locals中去掉os并不能起到限制。而且,os库是python中的标准库,只要有python就一定有os库,用户必然能导入成功。

那针对这种隐患,有没有办法不让用户导包呢?

# 在globals中将__builtins__设置为None
eval("__import__('os').system('whoami')", {"__builtins__": None})

Output:

Traceback (most recent call last):
  File "C:/Users/xxx/Desktop/eval_demo.py", line 62, in <module>
    eval("__import__('os').system('whoami')", {"__builtins__": None})
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object is not subscriptable

用户自己在eval()函数中导包,是使用__import__()函数实现的。__import__()函数是python的内建函数,是用于动态导库的函数。

print(dir(__builtins__))

Output:

['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'WindowsError', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']

在python中,内建函数都在__builtins__模块中,在启动python时,python解释器就直接导入了__builtins__模块中的函数。__import__()函数就是__builtins__模块中的一员,也就是说,python解释器默认导入了__import__()函数,用户可以直接调用。

上面的代码在globals参数中将__builtins__设置成None,eval()函数就获取不到__import__()函数了,无法自己导包,执行代码报错。

但是,限制用户导包,恶意用户还可以通过其他途径获取到os库。

s = '[x for x in ().__class__.__bases__[0].__subclasses__() if x.__name__ == "zipimporter"][0]("C:/Users/xxx/Lib/site-packages/setuptools-28.8.0-py3.6.egg").load_module("setuptools").os.system("whoami")'
eval(s, {"__builtins__": None})

Output:

desktop-xxx\xxx

上面这种方式也可以成功执行系统命令。代码中利用__class__和__subclasses__动态加载了基类object的所有子类(可以执行下面这行代码查看当前环境中基类都有哪些子类),然后找到了zipimporter,用zipimporter动态加载setuptools库的 .egg包,再链式调用load_module()成功导入setuptools库,从而成功调用os库执行系统命令。

print([x.__name__ for x in ().__class__.__bases__[0].__subclasses__()])

在python中,有一些库中内置了os库,导入这些库后就能调用os库,其中就包含setuptools,此外还有configobj、urllib、urllib2等。

当然,执行上面的代码需要有对应的.egg包,如果你也想演示看效果,你可以先在自己的电脑磁盘中全局搜一下,找不到再到网络下载。

以上是eval()函数的一些安全隐患,可谓是防不胜防,在写代码时无形中就需要和恶意用户进行很多回合的思维对抗,假如有哪个细节稍微考虑不周,就会留下很大的隐患。而且,关于eval(),恶意用户还有很多可以利用的方法,如删数据、暴力占满服务器的CPU资源等。

既然用了就防不胜防,那只有不用才不会留下隐患,所以,在一些企业和项目中就禁用了eval()函数。

(当然,python中也有替代方案,那就是ast.literal_eval()函数,ast.literal_eval()函数会判断字符串内容去掉首尾的引号后是不是合法的python类型,如果不是就报错,因此ast.literal_eval()函数也只能进行类型转换。)

以上就是本文的全部内容,希望能对你有帮助,欢迎点赞、收藏、评论和关注。

参考文档:
为什么说eval要慎用

推荐阅读
用Python代码自己写Python代码,竟如此简单

☟学Python,点击下方名片关注我。☟

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

功能强大,但因安全隐患被企业禁用的Python内置函数 的相关文章

随机推荐

  • 2023华为OD机试真题【施肥问题】

    题目描述 思路题解 首先需要计算每个果园的施肥时间 即果园面积除以施肥机能效 然后找到最小的施肥机能效 保证施肥任务能在规定时间内完成 如果施肥天数小于果园数量 则无法完成施肥任务 返回 1 如果施肥天数等于果园数量 则直接返回最大果园面积
  • 编译原理第七章笔记 -- 中间代码生成

    本文中内容整理西安交通大学软件学院吴晓军老师的ppt中 仅供学习使用 请勿转载或他用 参考教材 程序设计语言 编译原理 第3版 陈火旺等 国防工业出版社 这一章分数在35左右 两个大题 数组的引用四元式生成 控制语句当中布尔表达式的翻译 考
  • 运维必学

    欢迎关注 全栈工程师修炼指南 设为 星标 每天带你 基础入门 到 进阶实践 再到 放弃学习 专注 企业运维实践 网络安全 系统运维 应用开发 物联网实战 全栈文章 等知识分享 花开堪折直须折 莫待无花空折枝 作者 lt 安全开发运维 gt
  • VS2012编译安装VTK-5.10.1(支持Python)

    1 源码下载 到参考资料 1 下载vtk 5 10 1 zip和vtkdata 5 10 1 zip 2 源码解压 这里以D 盘为例进行说明 在D 盘中创建一个目录VTK 然后在其中创建4个目录 source build data和inst
  • mysql explain详解

    转自 http www blogjava net persister archive 2008 10 27 236813 html 在 explain的帮助下 您就知道什么时候该给表添加索引 以使用索引来查找记录从而让select 运行更快
  • Nginx反向代理与负载均衡

    文章目录 一 网关 代理与反向代理的关系 二 反向代理在系统架构中的应用场景 三 Nginx反向代理配置 1 不重定向配置 2 重定向配置 四 基于反向代理的负载均衡器 不支持https 五 负载均衡介绍 1 负载均衡策略 2 负载均衡调度
  • 三个java超级变态逻辑循环编程题

    1 有一根27厘米的细木杆 在第3厘米 7厘米 11厘米 17厘米 23厘米这五个位置上各有一只蚂蚁 木杆很细 不能同时通过一只蚂蚁 开始时 蚂蚁的头朝左还是朝右是任意的 它们只会朝前走或调头 但不会后退 当任意两只蚂蚁碰头时 两只蚂蚁会同
  • 【4】测试用例设计-判定表法

    判定表适用于有几个原因 导致几个结果的情况 实际测试中 如果输入条件较多 再加上各种输入与输出之间相互的作用关系 画出的因果图会比较复杂 容易使人混乱 为了避免这种情况 人们往往使用决策表法代替因果图法 决策表也称为 判定表 其实质就是一种
  • 各大公司薪资

    联合利华 MKT 9500 3000元安家费 普通职位 8KX12 联合利华销售代表 底薪加提成 总体一般 一般能拿到5K以上 宝洁 本8600 硕9700 博10500发14个月 11年数据 欧莱雅 MKT 6 6K X 13 11年数据
  • ajax工作原理 网页从输入url到呈现过程(TCP ,渲染引擎) 头像上传 下拉加载 节流 防抖 常见状态码

    Ajax工作原理 1 http网络传输协议 规定 前后端交互的 数据传输格式 协议 规定 前后端交互的数据传输格式 2 http协议组成两个部分 2 1前端 必须发送 请求报文格式 2 2后端 必须响应 响应报文格式 3 请求报文格式组成
  • VueX是什么?好处?何时使用?

    VueX相关 1 VueX是什么 2 使用VueX统一管理状态的好处 3 什么样的数据适合存储到Vuex中 1 VueX是什么 VueX是实现组件全局状态 数据 管理的一种机制 可以方便的实现组件之间数据的共享 如果没有VueX实现数据间的
  • 点云配准(四) Sparse Point Registration 算法浅析

    Sparse Point Registration SPR 是一篇2017年的点云配准算法 该算法的主要目的是对稀疏点云进行配准 并且取得了不错的成果和突破 本文一方面是对SPR配准算法模型进行了简单的原理解析以及附加代码实现 另一方面是对
  • c++ 的 multiple definition of `XXX‘

    文章目录 一 为什么会有多重定义问题 二 一些错误情景 1 头文件忘记加条件编译 2 类的静态变量在头文件定义 3 全局变量在 h文件定义 4 待补充 三 参考链接 一 为什么会有多重定义问题 声明 指出存储类型 并给出存储单元指定名称 定
  • C语言博客作业--嵌套循环

    一 PTA实验作业 题目1 7 1 查询水果价格 给定四种水果 分别是苹果 apple 梨 pear 桔子 orange 葡萄 grape 单价分别对应为3 00元 公斤 2 50元 公斤 4 10元 公斤 10 20元 公斤 首先在屏幕上
  • JavaScript函数的命名方式

    函数的命名方式 JavaScript代码服用单位是函数 函数可以包含一段可执行代码 也可以接受调用者传入的参数 JavaScript定义函数主要有以下三种方式 第一种方式 命名函数 第二种 匿名函数
  • 【C++】函数的设计与使用(三)重载函数、模板函数

    重载函数 参数列表不相同 可能是参数类型不相同 或者参数个数不相同 都不相同 也可以 的两个或多个函数 可以拥有相同的函数名称 编译器会把实参和每个重载函数的形参比对 找出哪个重载函数合适 所以每个重载函数的参数列表必须和其他的重载函数的不
  • ubuntu安装bochs,nasm

    1 ubuntu上安装bochs nasm 1 1 安装缘由 最近想自己做个操作系统玩一玩巩固巩固知识 工欲善其事 必先利其器 开发操作系统首先得搭建环境 编程语言上我选择C和汇编完成 开发环境是在我装的一个虚拟机ubuntu上 ubunt
  • 【满分】【华为OD机试真题2023 JAVA&JS】统计匹配的二元组个数

    华为OD机试真题 2023年度机试题库全覆盖 刷题指南点这里 统计匹配的二元组个数 知识点数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定两个数组A和B 若数组A的某个元素A i 与数组B中的某个元素B j 满足 A
  • NodeJS - Express使用

    文章目录 1 参数 1 1 获取URL中的动态参数 2 静态资源 2 1 挂载路径前缀 3 nodemon 4 1路由 4 1 路由的匹配过程 4 2 模块化路由 4 3 为路由模块添加前缀 5 中间件 5 1 全局生效的中间件 5 2 全
  • 功能强大,但因安全隐患被企业禁用的Python内置函数

    功能强大 但却因安全隐患被企业禁用的Python内置函数 eval 函数是Python的内置函数 功能非常强大 但是存在不小的安全隐患 有些企业或项目出于安全考虑 禁止使用eval 函数 会在一些安全相关的扫描校验中进行识别和拦截 杜绝使用