Python中不安全的反序列化操作示例
作者:保持持续学习
1.Python 的 pickling 和 unpickling?
pickling 和 unpickling 是指序列化和反序列化对象的过程。具体来说,pickling 是将一个 Python 对象转换为一个字节流,以便可以将其存储到文件中或通过网络传输;而 unpickling 是将字节流重新转换回原来的 Python 对象。
1)Pickling 示例操作: 为了实现 pickling,我们通常会使用 pickle 模块。例如:
import pickle
data = {"name": "Alice", "age": 25, "is_student": True}
# 将对象序列化并写入文件
with open("data.pickle", "wb") as f:
pickle.dump(data, f)2)Unpickling 示例操作: 对应地,要进行 unpickling,可以这样操作:
import pickle
# 从文件中读取字节流并反序列化为对象
with open("data.pickle", "rb") as f:
data = pickle.load(f)
print(data) # 输出:{'name': 'Alice', 'age': 25, 'is_student': True}3)安全性问题: 值得注意的是,使用 pickle 时要小心安全问题。因为 pickle 可以执行任意代码,所以从不可信来源加载 pickle 数据是危险的,可能导致代码执行漏洞。因此,尽量避免加载未经验证的 pickle 文件。
4)絕對 file path 和相對文件存取: 在实际应用中,我们可能会使用相对路径来读取或写入 pickle 文件,以增强代码的可移植性。例如:data.pickle 相较于绝对路径 "/path/to/data.pickle" 更方便在不同环境下运行。
5)pickle 模块的替代品: 如果需要更安全的序列化方法,可以考虑使用 json 模块,它只支持基本数据类型的序列化和反序列化,并不执行任何代码。因此,它是更安全的选择,尽管功能上没有 pickle 强大。
import json
data = {"name": "Alice", "age": 25, "is_student": True}
# 序列化
with open("data.json", "w") as f:
json.dump(data, f)
# 反序列化
with open("data.json", "r") as f:
data = json.load(f)
print(data) # 输出:{'name': 'Alice', 'age': 25, 'is_student': True}2.如何利用pickle模块的反序列化来执行恶意代码?
简单来说,pickle 之所以不安全,是因为它不仅仅是在存储数据,它还在存储**“如何构建对象”的指令**。
1. 为什么 pickle 是不安全的?
pickle 的设计目标是序列化几乎任何 Python 对象。为了实现这一点,它使用了一种名为 Stack Machine(栈机) 的机制。
当你“反序列化”(unpickling)一个文件时,你实际上是在运行一段 Pickle 虚拟机代码。pickle 模块会盲目地执行这些指令来重建对象。如果指令说“去运行这个系统命令”,Python 就会照办。
核心风险点:
pickle.load()并没有任何沙箱保护。它信任它读取的所有数据。
2. 为什么可以执行任意代码?
这主要归功于 Python 对象协议中的一个特殊魔术方法:__reduce__。
当 pickle 尝试序列化一个对象时,它会检查该对象是否有 __reduce__ 方法。如果有,该方法必须返回一个元组。这个元组最常见的情况包含两个元素:
- 一个可调用对象(函数)。
- 一个参数元组,供上述函数调用。
在反序列化时,pickle 会自动调用这个函数并传入参数。这就是攻击点。
3. Python 例子演示
让我们通过代码来看看如何构建一个“恶意”的 pickle 包。
下面的代码创建了一个类,它的 __reduce__ 方法指示 pickle 在解封时运行系统命令(例如在 Windows 上打开计算器,或在 Linux/Mac 上创建一个文件)。
import pickle
import os
class MaliciousPickle:
def __reduce__(self):
# os.system 是我们要调用的函数
# ('calc',) 是传给它的参数(Windows 下打开计算器)
# 如果是 Linux/Mac,可以改为 ('touch /tmp/hacked.txt',)
return (os.system, ('calc',))
# 1. 序列化:黑客生成恶意的 pickle 数据
evil_data = pickle.dumps(MaliciousPickle())
# 2. 假设我们将 evil_data 发送给了受害者
# 3. 反序列化:受害者仅仅是想“读取数据”
print("正在解封数据...")
pickle.loads(evil_data) # 此时,计算器程序会被弹出!在上面的例子中,pickle.loads() 本意是恢复一个对象,但它却执行了 os.system('calc')。
- 如果黑客把命令换成
rm -rf /(删除系统文件)。 - 或者换成一个反弹 Shell(Reverse Shell),远程控制你的服务器。
- 你的程序甚至不需要以管理员权限运行,只要它有权限执行的操作,黑客都能做。
4. 如何防范?
- 永远不要
pickle.load()来自不信任来源的数据。 这是金科玉律。 - 使用更安全的格式: 优先选择
JSON、XML或MessagePack。这些格式只存储纯数据,不包含执行逻辑。 - 签名验证: 如果必须使用
pickle,请使用hmac对数据进行签名,确保数据在传输过程中没有被篡改。
pickle 是为了方便和灵活而设计的,但这种灵活性赋予了数据“生命”,让它在被加载时能执行动作。在安全领域,**“数据与指令混淆”**通常是漏洞的根源。
3.Json模块的反序列化为啥是安全的?
json 模块之所以被认为是安全的,核心原因在于它的本质属性和解析机制与 pickle 完全不同。
我们可以从以下三个维度来拆解为什么 JSON 不会像 pickle 那样“反客为主”:
1. 数据 vs 指令 (Data vs. Code)
这是最根本的区别。
- Pickle 是一种“脚本”:
pickle序列化的结果实际上是一串给 Pickle 虚拟机运行的指令。它包含“创建对象”、“查找模块”、“调用函数”等操作。正如我们之前看到的,__reduce__就像是在数据包里藏了一张“指令单”。 - JSON 是“纯描述”: JSON(JavaScript Object Notation)是一种声明式的数据交换格式。它只能表示有限的几种数据类型:字符串、数字、布尔值、null、数组(列表)和对象(字典)。
打个比方:
- Pickle 就像是你收到一个包裹,里面有一张纸条写着:“去厨房打开煤气灶”。如果你照做了,就会发生动作。
- JSON 就像是你收到一张清单,上面写着:“苹果:5个,香蕉:3个”。这张纸条本身没有任何权力让你去做动作,它只是在描述状态。
2. 解析器的局限性
json.loads() 的工作原理是极其受限的。
当你运行 json.loads(data) 时,Python 的 JSON 解析器只会在内存中构建基础的内置类型(如 dict, list, str)。它完全不知道什么是 Python 类,也没有机制去触发 os.system() 这样的外部系统调用。
在解析 JSON 时,即便你在 JSON 字符串里写了类似代码的内容:
{
"command": "os.system('rm -rf /')"
}解析器也只会把它当作一个普通的字符串。除非你自己在代码里显式地拿到这个字符串并传给 eval() 执行,否则它就只是一串死板的字符。
3. 缺乏“对象重建”机制
pickle 之所以危险,是因为它支持自动重建自定义对象。
- 在
pickle中,如果数据说它是MyClass类型,pickle会尝试导入这个类并实例化它。 - 在
json中,没有任何语法可以指定“这是一个某个类的实例”。JSON 只知道[]是数组,{}是键值对。
比较表
| 特性 | Pickle | JSON |
| 本质 | 二进制指令流(可执行) | 文本字符串(纯数据) |
| 支持类型 | 几乎所有 Python 对象 | 仅基础类型 (str, int, dict, list等) |
| 自定义类 | 自动触发 __init__ 或 __reduce__ | 不支持,除非手动编写解析逻辑 |
| 安全性 | 不安全 (信任数据即信任代码) | 安全 (解析过程不涉及逻辑执行) |
4. 潜在的例外(安全警告)
虽然 json.loads() 本身是安全的,但作为开发者,如果处理不当仍会引入风险:
- 后期处理漏洞: 如果你把解析出来的 JSON 数据直接传给了
eval()、exec()或者数据库查询语句(SQL 注入),那么危险依然存在。 - 拒绝服务攻击 (DoS): 极度深层嵌套的 JSON(例如
[[[[...]]]])可能会消耗大量的 CPU 或内存资源,导致程序崩溃,但这属于稳定性问题,而非任意代码执行。
总结
JSON 的安全性源于它的简单性和被动性。它只是一面反映数据的镜子,而 pickle 却是一台藏在盒子里的微型计算机。
既然我们谈到了安全的数据交换,你想了解一下如何为你的 JSON 数据添加“数字签名”,以确保它在传输过程中没有被第三方篡改吗?
参考文档
https://docs.python.org/zh-cn/3/library/pickle.html
https://www.runoob.com/python3/python-pickle.html
https://www.datacamp.com/tutorial/pickle-python-tutorial
到此这篇关于Python中不安全的反序列化操作示例的文章就介绍到这了,更多相关Python不安全反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
