Python 标准库中的 pickle 模块提供了对象序列化与反序列化能力,可以将内存中的对象转换为字节流,也能将字节流还原为对象。这种机制在缓存、进程间通信和模型持久化等场景中非常方便,但其底层设计决定了它并不适合处理任何不可信的数据。

Pickle 的执行模型本质
pickle 并不是单纯的数据格式,而是一套“对象重建指令集”。在序列化时,pickle 会记录对象的类型、状态以及重建该对象所需调用的函数或方法。最核心的环节是对象可以通过定义 __reduce__ 方法,返回一个元组,其中第一个元素是可调用对象,第二个元素是传给它的参数。反序列化时,pickle 会真的去调用这个可调用对象。
这意味着,如果攻击者能够控制被反序列化的字节流,他就可以构造一个对象,使其 __reduce__ 返回 os.system 这样的危险函数以及对应参数。当受害者执行 pickle.loads 时,系统命令就会直接被执行。下面的代码展示了恶意 payload 的构造方式:
import pickle
import os
class Evil:
def __reduce__(self):
return (os.system, ('echo hacked > /tmp/pwned.txt',))
payload = pickle.dumps(Evil())
# 受害者侧执行以下代码即中招
pickle.loads(payload)
上述代码中,Evil 类的 __reduce__ 告诉 pickle:重建时请调用 os.system 并传入字符串参数。反序列化过程没有沙箱限制,因此命令会被原样执行。这种机制与 JSON 等纯数据交换格式有本质区别。
常见触发场景与危害
在实际工程中,Pickle 安全隐患常出现在多处。其一是使用 pickle 作为 Web 应用的会话存储格式,并将 cookie 或请求体中的内容直接反序列化;其二是从公网下载所谓的“模型文件”并用 pickle 加载,例如某些机器学习社区分享的 pt 或 pkl 文件;其三是多进程任务队列(如早期 Celery 配置)使用 pickle 序列化任务参数,且消息中间件未做鉴权。
一旦被利用,攻击者能做到的事情远超读取数据。他可以执行任意系统命令、读取服务器上的密钥文件、反弹 shell、甚至进一步横向移动。由于反序列化发生在业务代码之前,传统 WAF 或参数校验往往难以拦截这种攻击。下面是一段存在风险的 Flask 伪代码:
from flask import Flask, request
import pickle
app = Flask(__name__)
@app.route('/load')
def load():
data = request.args.get('data')
# 危险:直接反序列化用户传入的字节
obj = pickle.loads(bytes.fromhex(data))
return str(obj)
该接口把用户传入的十六进制字符串还原为字节后交给 pickle.loads,攻击者只需生成恶意 payload 的十六进制即可远程控制服务器。这类接口在上线前若未做安全评审,极易成为入侵入口。
安全替代与防护方案
如果需求只是传递数据结构而非复杂对象,优先使用 json 模块。JSON 只能表示字典、列表、字符串等基础类型,不具备代码执行能力。对于必须序列化自定义类的场景,可考虑 dataclasses 加 JSON 编码,或使用 marshal、jsonpickle 等受控方案,并严格限制类型。
当确实需要使用 pickle 时,应做到以下几点:仅反序列化来自可信通道且经过签名的数据;使用 HMAC 校验字节流完整性,防止被篡改;在加载前通过白名单限制可接受的类。下面示例展示了带签名校验的加载方式:
import pickle
import hmac
import hashlib
SECRET = b'my-secret-key'
def safe_loads(data, sig):
expect = hmac.new(SECRET, data, hashlib.sha256).digest()
if not hmac.compare_digest(expect, sig):
raise ValueError('签名校验失败')
return pickle.loads(data)
# 使用方先生成签名再传输
raw = pickle.dumps({'a': 1})
sig = hmac.new(SECRET, raw, hashlib.sha256).digest()
obj = safe_loads(raw, sig)
即便如此,签名只能防止数据被篡改,不能阻止拥有密钥的一方自己构造恶意对象。因此最根本的原则仍是:不要对不可信数据使用 pickle。在跨系统、跨信任边界的通信中,选择语言无关且表达能力受限的序列化协议,如 JSON、MessagePack 或 Protocol Buffers,才是更稳健的做法。
总结
Pickle 的安全隐患来源于其“可执行的反序列化”本质,而非实现缺陷。开发者在享受其便利的同时,必须清楚区分信任边界。对内临时缓存和对外部输入处理应采用完全不同的技术选型,避免将强大的对象重建能力暴露给潜在攻击者。