Python中有潜在代码执行风险的函数(一)
字数 1190 2025-08-25 22:58:46
Python中有潜在代码执行风险的函数(一) - eval与exec详解
1. 简介
在Python中,eval和exec是两个存在任意代码执行隐患的重要函数,它们都可以执行传入的字符串代码,但有着不同的特性和用途。
eval函数
- 内置函数,语法:
eval(expression, globals=None, locals=None) - 接收三个参数:
expression:用作运算的字符串类型表达式globals:指定运行时的全局命名空间(可选)locals:指定运行时的局部命名空间(可选)
- 返回表达式的计算结果
exec函数
- Python 2中是内置语句,Python 3中整合为函数
- 语法:
exec(object[, globals[, locals]]) - 可以执行复杂的代码逻辑(如变量赋值)
- 返回值永远为None
- 同样接受globals和locals参数
2. eval与exec的区别
执行能力不同
exec可以执行完整的Python语句(如import os)eval只能执行表达式(如__import__('os'))
返回值不同
eval返回表达式的计算结果exec不返回任何值
赋值操作
exec可以对变量进行赋值eval不能进行赋值操作
3. 安全隐患
当传入eval或exec的参数可控时,可能导致任意代码执行漏洞。
示例漏洞代码
def addition(a, b):
return eval(f"{a}+{b}")
攻击者可传入恶意参数:
addition("__import__('os').system('whoami')#", "2")
这将执行系统命令whoami
4. 防范措施
限制globals参数
通过指定globals参数限制可用的内置函数:
eval("expression", {'__builtins__': None})
原理
__builtins__模块提供对Python所有内建标识符的访问- 限制
__builtins__可以阻止直接使用危险函数(如import)
5. 绕过限制的方法
即使限制了__builtins__,攻击者仍可通过对象继承链绕过:
利用继承链
().__class__.__bases__[0].__subclasses__()[176]("whoami")
解释:
().__class__获取元组的类(tuple)__bases__[0]获取父类(object)__subclasses__()获取所有子类- 找到可执行命令的子类(如
subprocess.Popen,索引176)
其他变体
[].__class__.__bases__[0].__subclasses__()[176]("whoami")
"".__class__.__bases__[0].__bases__[0].__subclasses__()[176]("whoami")
6. 更安全的替代方案
使用ast.literal_eval()
- 只允许安全的数据类型:
- 字符串、字节、数字
- 元组、列表、字典、集合
- 布尔值、None
- 拒绝执行任何函数调用或语句
示例:
import ast
ast.literal_eval("[1, 2, 3]") # 安全
ast.literal_eval("__import__('os')") # 抛出异常
7. 总结
eval和exec都存在代码执行风险,应谨慎使用- 简单的
__builtins__限制可能被绕过 - 优先考虑使用
ast.literal_eval()等更安全的替代方案 - 如果必须使用
eval/exec,应严格控制输入并实施多层防护
后续文章将介绍Python中其他存在安全隐患的函数。