Python中有潜在代码执行风险的函数(一)
字数 1190 2025-08-25 22:58:46

Python中有潜在代码执行风险的函数(一) - eval与exec详解

1. 简介

在Python中,evalexec是两个存在任意代码执行隐患的重要函数,它们都可以执行传入的字符串代码,但有着不同的特性和用途。

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. 安全隐患

当传入evalexec的参数可控时,可能导致任意代码执行漏洞。

示例漏洞代码

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")

解释:

  1. ().__class__获取元组的类(tuple
  2. __bases__[0]获取父类(object
  3. __subclasses__()获取所有子类
  4. 找到可执行命令的子类(如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. 总结

  • evalexec都存在代码执行风险,应谨慎使用
  • 简单的__builtins__限制可能被绕过
  • 优先考虑使用ast.literal_eval()等更安全的替代方案
  • 如果必须使用eval/exec,应严格控制输入并实施多层防护

后续文章将介绍Python中其他存在安全隐患的函数。

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 的参数可控时,可能导致任意代码执行漏洞。 示例漏洞代码 攻击者可传入恶意参数: 这将执行系统命令 whoami 4. 防范措施 限制globals参数 通过指定 globals 参数限制可用的内置函数: 原理 __builtins__ 模块提供对Python所有内建标识符的访问 限制 __builtins__ 可以阻止直接使用危险函数(如 import ) 5. 绕过限制的方法 即使限制了 __builtins__ ,攻击者仍可通过对象继承链绕过: 利用继承链 解释: ().__class__ 获取元组的类( tuple ) __bases__[0] 获取父类( object ) __subclasses__() 获取所有子类 找到可执行命令的子类(如 subprocess.Popen ,索引176) 其他变体 6. 更安全的替代方案 使用ast.literal_ eval() 只允许安全的数据类型: 字符串、字节、数字 元组、列表、字典、集合 布尔值、None 拒绝执行任何函数调用或语句 示例: 7. 总结 eval 和 exec 都存在代码执行风险,应谨慎使用 简单的 __builtins__ 限制可能被绕过 优先考虑使用 ast.literal_eval() 等更安全的替代方案 如果必须使用 eval/exec ,应严格控制输入并实施多层防护 后续文章将介绍Python中其他存在安全隐患的函数。