python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python不安全反序列化

Python中不安全的反序列化操作示例

作者:保持持续学习

本文给大家介绍了Python的pickling和unpickling过程,详细解释了如何利用pickle模块的反序列化来执行恶意代码,以及如何防范这种风险,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧

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

4. 如何防范?

pickle 是为了方便和灵活而设计的,但这种灵活性赋予了数据“生命”,让它在被加载时能执行动作。在安全领域,**“数据与指令混淆”**通常是漏洞的根源。

3.Json模块的反序列化为啥是安全的?

json 模块之所以被认为是安全的,核心原因在于它的本质属性解析机制pickle 完全不同。

我们可以从以下三个维度来拆解为什么 JSON 不会像 pickle 那样“反客为主”:

1. 数据 vs 指令 (Data vs. Code)

这是最根本的区别。

打个比方:

2. 解析器的局限性

json.loads() 的工作原理是极其受限的。

当你运行 json.loads(data) 时,Python 的 JSON 解析器只会在内存中构建基础的内置类型(如 dict, list, str)。它完全不知道什么是 Python 类,也没有机制去触发 os.system() 这样的外部系统调用。

在解析 JSON 时,即便你在 JSON 字符串里写了类似代码的内容:

{
    "command": "os.system('rm -rf /')"
}

解析器也只会把它当作一个普通的字符串。除非你自己在代码里显式地拿到这个字符串并传给 eval() 执行,否则它就只是一串死板的字符。

3. 缺乏“对象重建”机制

pickle 之所以危险,是因为它支持自动重建自定义对象

比较表

特性PickleJSON
本质二进制指令流(可执行)文本字符串(纯数据)
支持类型几乎所有 Python 对象仅基础类型 (str, int, dict, list等)
自定义类自动触发 __init____reduce__不支持,除非手动编写解析逻辑
安全性不安全 (信任数据即信任代码)安全 (解析过程不涉及逻辑执行)

4. 潜在的例外(安全警告)

虽然 json.loads() 本身是安全的,但作为开发者,如果处理不当仍会引入风险:

总结

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不安全反序列化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文