pickle本身不安全,不能用于不可信数据;其反序列化会重放构造逻辑,调用__reduce__等魔术方法执行任意代码,如os.system或eval,攻击者仅需构造恶意字节流即可触发命令执行。

直接说结论:pickle 本身**不安全**,不能用于不可信数据;所谓“安全使用”指的是严格控制输入来源、避免反序列化外部数据,并在必须用时加隔离和校验。
为什么 pickle.load() 会执行任意代码
pickle 反序列化不是单纯还原对象,而是重放构造逻辑——它会调用 __reduce__、__setstate__ 等魔术方法,甚至可导入并执行模块中的函数。攻击者只要构造恶意字节流,就能触发 os.system 或 eval。
- 常见错误现象:
pickle.loads(b"cos\nsystem\n(S'whoami'\ntR.")在某些 Python 版本下直接执行系统命令 - 真实场景:从文件/网络读取 unpickle 数据前,未确认来源是否可信(如用户上传的
.pkl文件) - 兼容性影响:Python 3.8+ 默认仍启用 full protocol,无自动降级或沙箱机制
替代方案比“加固 pickle”更可靠
除非你完全掌控数据生成与消费两端(比如同一进程内 cache),否则优先选不会执行代码的格式:
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 结构化数据 → 用
json(配合dataclasses.asdict()或pydantic.BaseModel.dict()) - 需要二进制效率 → 用
msgpack(pip install msgpack,支持datetime、bytes,且无执行风险) - 科学计算场景 →
numpy.save()/numpy.load()或torch.save()(专用于其自身对象,不通用但安全) - 若必须保留 pickle 接口 → 改用
dill并禁用exec类操作(仍不推荐用于外部输入)
如果非用不可,至少做三件事
仅限内部服务、测试环境或 CLI 工具中自己生成又自己读取的场景:
立即学习“Python免费学习笔记(深入)”;
- 限定协议版本:始终用
protocol=4(Python 3.7+ 默认)或protocol=5,避免旧版中更松散的解析逻辑 - 预检字节流头:检查前 2 字节是否为
b'\x80\x04'(proto 4)或b'\x80\x05'(proto 5),跳过明显异常开头 - 用自定义
Unpickler限制可导入模块:class SafeUnpickler(pickle.Unpickler): def find_class(self, module, name): if module not in ("__builtin__", "builtins", "numpy", "collections"): raise pickle.UnpicklingError(f"Forbidden module: {module}") return super().find_class(module, name)注意:这只能防 import,不能防getattr动态调用或已加载模块内的危险方法
最常被忽略的一点:即使做了上述限制,只要对象里存了 lambda、functools.partial 或带 __reduce__ 的自定义类,就仍可能触发意外行为。安全边界不在反序列化函数,而在整个对象图的构造逻辑是否可控。

















