pickle反序列化会执行任意代码,因其本质是记录并重放对象重建的构造指令流,攻击者可通过控制输入注入__reduce__恶意调用;它本就只应限于可信环境使用。

为什么 pickle 反序列化会执行任意代码
pickle 不是数据格式协议,而是一套 Python 对象的“运行时快照”机制。它序列化时记录的是对象类型、属性值、以及重建该对象所需的**构造指令流**(类似字节码),反序列化时直接在当前解释器中执行这些指令。
这意味着只要攻击者能控制输入字节流,就能注入 __reduce__ 方法返回恶意函数调用,比如 os.system 或 eval。这不是 bug,而是设计使然——pickle 本就只应在可信环境中使用。
- 永远不要用
pickle.load()解析来自网络、文件或用户输入的未知数据 - 替代方案:用
json(仅支持基础类型)、dataclasses.asdict()+json、或msgpack(需显式注册类) - 若必须用
pickle,可用自定义Unpickler重写find_class,白名单限制可加载的模块和类
json 序列化失败常见原因与绕过方式
json.dumps() 默认只接受 str、int、float、bool、None、list、dict 这七种类型。遇到 datetime、bytes、自定义类实例时直接报 TypeError: Object of type ... is not JSON serializable。
解决核心思路是提前把非标量对象“降维”成 JSON 可表示的结构:
立即学习“Python免费学习笔记(深入)”;
本文档主要讲述的是JSON.NET 简单的使用;JSON.NET使用来将.NET中的对象转换为JSON字符串(序列化),或者将JSON字符串转换为.NET中已有类型的对象(反序列化?)。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
- 用
default参数传入转换函数,例如处理datetime:lambda obj: obj.isoformat() if isinstance(obj, datetime) else None - 对
bytes,先obj.decode('utf-8')(确保是文本)或base64.b64encode(obj).decode('ascii') - 避免重写
JSONEncoder子类——除非需要复用或统一规则;临时场景用default更轻量 - 注意:
json不保留类型信息,反序列化后得靠业务逻辑手动还原为datetime等对象
自定义类如何支持 __getstate__ 和 __setstate__
默认情况下,pickle 保存对象的整个 __dict__。但有些字段不该存(如打开的文件句柄、缓存、线程锁),有些字段需要特殊重建逻辑(如重建数据库连接)。
这时要显式定义 __getstate__(决定序列化哪些内容)和 __setstate__(决定反序列化时如何初始化):
def __getstate__(self):
state = self.__dict__.copy()
# 排除不可序列化的字段
state.pop('cache', None)
state.pop('_db_conn', None)
return state
def setstate(self, state):
self.dict.update(state)
手动重建资源
self._db_conn = self._connect_db()
-
__getstate__必须返回一个 pickleable 对象(通常是 dict) -
__setstate__接收的就是这个对象,不一定要调用__dict__.update,也可完全重新构造 - 如果类有
__slots__,__dict__不存在,需改用getattr(self, attr)显式提取字段
不同序列化方式的性能与兼容性取舍
没有“最好”的序列化方式,只有“更适合当前场景”的选择。关键权衡点是:语言互通性 vs Python 特性支持 vs 体积 vs 速度。
-
pickle:最快、支持所有 Python 类型和引用关系,但仅限 Python,且不安全;适合进程内缓存或可信 RPC -
json:跨语言、人可读、安全,但丢精度(如 int/float 混淆、无 datetime 原生支持)、体积大;适合 API 响应、配置文件 -
msgpack:二进制、比 JSON 小且快,支持扩展类型(需注册),但默认不支持 Python 特有对象;适合微服务间高效通信 -
pydantic.BaseModel.model_dump_json():自动处理嵌套模型 + datetime + enum,但依赖 pydantic,生成的是 JSON 字符串;适合现代 Python Web 后端
真正容易被忽略的是引用循环和共享对象——pickle 能正确处理,json 直接报错,msgpack 默认也不支持;如果数据结构里有 a.x = b; b.y = a 这种,得提前检测或扁平化。








