不能直接用 pickle.loads() 处理不受信数据,因为其反序列化会执行任意代码(如 __reduce__、导入模块、调用函数),导致远程命令执行等严重风险;必须使用白名单限制类型或改用 JSON 等安全格式。

为什么不能直接用 pickle.loads() 处理不受信数据
因为 pickle 反序列化会执行任意类的 __reduce__ 方法、调用任意函数、导入任意模块——只要数据里写了,它就照做。这不是漏洞,是设计如此。你传入一个伪造的字节串,就能让服务器执行 os.system("rm -rf /"),真实案例已多次出现在CTF和生产事故中。
常见错误现象包括:服务突然崩溃、CPU飙升、文件被删、反向shell连出。这些都不是“解析失败”,而是代码已执行。
- 所有来自网络、文件上传、数据库读取、用户输入的 pickle 数据,都视为不可信
-
pickle.load()和pickle.loads()在无额外防护时等价危险 - 即使你只期望反序列化
dict或list,攻击者仍可构造伪装成 dict 的恶意对象
用 RestrictedUnpickler 限制可加载类型
Python 标准库不提供白名单式反序列化,但你可以子类化 pickle.Unpickler 并重写 find_class(),只允许预定义的安全类型。
实操建议:
- 只放行内置类型(
dict、list、tuple、str、int、float、bool、NoneType),不放行任何自定义类或模块级函数 - 显式禁止
builtins.exec、builtins.eval、os.system等高危名称,哪怕它们没在白名单里也需二次拦截 - 注意
__import__是内置函数,会被 pickle 自动调用,必须在find_class中拦截字符串形式的模块名
示例:
import pickle
import builtins
<p>class RestrictedUnpickler(pickle.Unpickler):
SAFE_BUILTINS = {"dict", "list", "tuple", "str", "int", "float", "bool", "NoneType"}
SAFE_MODULES = {"<strong>builtin</strong>": SAFE_BUILTINS, "builtins": SAFE_BUILTINS}</p><pre class="brush:php;toolbar:false;">def find_class(self, module, name):
if module in self.SAFE_MODULES and name in self.SAFE_MODULES[module]:
return getattr(builtins, name)
raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden")使用
data = b'\x80\x04\x95\x0f\x00\x00\x00\x00\x00\x00\x00}q\x00(X\x03\x00\x00\x00fooq\x01K\x01u.' try: obj = RestrictedUnpickler(io.BytesIO(data)).load() except pickle.UnpicklingError as e: print("拒绝反序列化:", e)
更可靠的选择:换格式,别用 pickle
如果你控制得了序列化端,最安全的做法是根本不用 pickle。JSON、MessagePack、TOML、YAML(禁用 !!python/ 标签)都比 pickle 更适合传输数据。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
使用场景判断:
- 仅需传结构化数据(如配置、API响应)→ 用
json.loads(),天然无执行风险 - 需要二进制紧凑 + 跨语言 → 用
msgpack.unpackb(data, strict_map_key=False),默认不支持代码执行 - 必须兼容旧 pickle 接口且无法改发送方 → 必须上
RestrictedUnpickler,并加一层内存隔离(如沙箱进程)
注意:yaml.load() 默认使用 FullLoader,但旧版本默认是 UnsafeLoader,务必显式指定 yaml.safe_load();而 json 没有 loader 选项,天生安全。
如果非得用 pickle,至少加校验和与超时
白名单机制能防大部分攻击,但绕过手段仍在演进(比如利用 collections.OrderedDict 的内部逻辑触发副作用)。因此需叠加防御层。
实操建议:
- 对 pickle 数据计算
hmac或blake2b,只反序列化带有效签名的数据 - 在子进程中运行反序列化(
multiprocessing.Process+timeout),防止无限循环或 fork bomb - 用
resource.setrlimit(resource.RLIMIT_CPU, (1, 1))限制 CPU 时间,避免计算密集型 gadget - 禁止反序列化超过 1MB 的数据,防止大对象耗尽内存
复杂点在于:pickle 的安全性不是“开个开关”就能解决的事,它是信任模型问题。一旦你决定接受 pickle 输入,就得为每个字节负责——包括它可能触发的 C 扩展行为、内存布局副作用、甚至 JIT 编译器漏洞。很多团队最后发现,改成 JSON + Pydantic 验证,比修 pickle 沙箱更快更稳。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










