直接回答:pickle 不是纯数据格式,而是一套栈式虚拟机指令流,反序列化时会调用对象 __reduce__ 返回的任意可调用对象来重建实例——攻击者可以构造指令让进程执行任意代码,等价于远程代码执行(RCE)。官方文档明确警告:永远不要 unpickle 不可信来源的数据,签名验证之外的任何"过滤"都不可靠。
import pickle, os
class Evil:
def __reduce__(self):
return (os.system, ("id > /tmp/pwned",))
payload = pickle.dumps(Evil())
pickle.loads(payload) # 反序列化即执行命令
展开解析:真实风险场景包括:缓存或消息队列里存 pickle 且通道可被篡改;加载第三方分享的模型文件,PyTorch 的 .pt/.pth 默认就是 pickle 格式,这也是供应链攻击的常见载体;多进程、分布式任务队列用 pickle 传对象,边界一旦扩大就埋雷。选型建议:跨语言、不可信数据用 JSON(只支持基础类型)或 MessagePack;数组数据用 numpy.load(..., allow_pickle=False)(新版本默认 False);大模型权重社区已转向 safetensors,纯张量布局、零代码执行面。必须在内网可信边界内用 pickle 时,给数据附 HMAC 签名,先验签再 loads。
实践要点:JSON 不能直接序列化 datetime、set、自定义对象,需要自定义 encoder;pickle 还有版本兼容问题(protocol 0–5,5 支持 out-of-band buffer),跨 Python 版本共享数据要固定 protocol 并记录版本。
追问方向:PyTorch 的 weights_only=True 是怎么缓解这个问题的?为什么"先扫描 pickle 指令再执行"的过滤方案不可靠?
(约 540 字)