




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 可表示的结构:
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,且不安全;适合进程内缓存或可信 RPCjson:跨语言、人可读、安全,但丢精度(如 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 这种,得提前检测或扁平化。