Python 深入浅出:数据序列化 json 与 pickle 的安全与选型
在进行网络通信、本地数据持久化或者分布式任务调度(如 Celery)时,我们必须要将内存中的 Python 对象转换为可以保存或网络传输的字节/文本格式。这个过程被称为序列化(Serialization)。
Python 标准库提供了两个最常用的序列化模块:
1. json:跨语言的轻量级文本序列化协议。
2. pickle:Python 专用的强力二进制序列化协议。
然而,许多开发者在进行工具选型时,往往忽视了它们之间的安全性差异。盲目使用 pickle 来反序列化外部传来的数据,会导致极其致命的“远程代码执行(RCE)”漏洞。
本文将带您剖析这两大模块的底层差异、选型原则以及至关重要的安全红线。
一、 跨平台标准:json 模块
json 采用人类可读的纯文本格式,是当今互联网 API 交互的绝对标准。
核心特征:
- 跨语言:支持所有的现代编程语言。
- 数据限制:仅支持最基本的 Python 数据类型(
dict、list、str、int、float、bool、None)。 - 不支持自定义类:如果你尝试
json.dumps(User())序列化一个自定义类实例,会直接抛出TypeError异常(需要通过自定义JSONEncoder才能支持)。
二、 Python 独享二进制:pickle 模块
pickle(泡菜)是 Python 特有的序列化引擎。它几乎能够将任意复杂的 Python 对象(包括自定义类的实例、深层嵌套的树/图结构、甚至是函数和类定义本身)原封不动地打包成二进制字节流。
核心特征:
- 仅限 Python:其他语言无法轻松解析
pickle数据。 - 支持任意对象:无需任何额外配置,直接支持自定义复杂类。
三、 致命死穴:pickle 反序列化远程代码执行(RCE)漏洞
pickle.loads(data) 隐藏着极具破坏性的安全漏洞。
漏洞机理分析:
在反序列化(重建对象)时,pickle 允许类通过重写内置方法 __reduce__ 来控制其重建行为。__reduce__ 可以返回一个可执行函数以及参数。当反序列化被触发时,Python 解释器会自动在后台调用执行这个函数。
如果恶意黑客伪造了 pickle 字节流,并将 __reduce__ 关联为 os.system('rm -rf /') 或木马下载指令,只要你的代码调用了 pickle.loads(),服务器就会立刻中招被黑!
漏洞代码复现(警示演示):
import pickle
import os
class MaliciousPayload:
def __reduce__(self):
# 声明在反序列化时,自动调用系统命令打开计算器(或下载木马)
return (os.system, ('calc.exe',))
# 1. 黑客在本地生成恶意字节流
payload_bytes = pickle.dumps(MaliciousPayload())
# 2. 你的服务器收到数据后,毫无防备地调用了 loads
print("⚠️ 正在反序列化未经验证的数据...")
pickle.loads(payload_bytes) # 此时计算器直接被弹出,意味着系统已被攻破!
铁律:永远不要对任何来自客户端、Cookie、未经验证的网络请求或外部上传的 pickle 二进制数据调用 loads()!
四、 优雅安全的自定义对象 json 序列化
如果确实需要传输或保存自定义类对象,推荐使用更安全的 json 并通过继承 json.JSONEncoder 来完成编码:
import json
class User:
def __init__(self, name, age):
self.name = name
self.age = age
# 1. 自定义编码器
class UserEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, User):
# 将自定义对象拆解为基础 dict 结构
return {"__type__": "User", "name": obj.name, "age": obj.age}
return super().default(obj)
# 2. 自定义解码器
def user_decoder(dct):
if "__type__" in dct and dct["__type__"] == "User":
return User(dct["name"], dct["age"])
return dct
# ==================== 测试运行 ====================
user = User("Alice", 25)
# 序列化:传入自定义编码器类
json_str = json.dumps(user, cls=UserEncoder)
print("JSON 字符串:", json_str)
# 反序列化:传入自定义解码钩子函数
new_user = json.loads(json_str, object_hook=user_decoder)
print(f"反序列化成功: {new_user.name} | 年龄: {new_user.age}")
五、 总结与选型黄金法则
- 外部通信,只用
json:所有暴露给外部接口、Web API、客户端的数据交互,严禁使用pickle,必须使用json或Protocol Buffers等安全格式。 - 内部受信环境,才用
pickle:只有在 100% 受信的内部环境下(如你自己的分布式节点间通信、本地安全缓存),且序列化对象过于复杂时,才考虑使用pickle。 - 数据校验先行:反序列化前,始终进行严格的权限校验和签名校验(如使用 HMAC),确保数据中途未被黑客篡改。
筑牢序列化安全防线,是避免系统线上被攻破沦为肉鸡的关键基础!
本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。



暂无评论
还没有人评论过本文,快来发表你的高见吧!