缓存里取出的 session 用 pickle.loads 还原,某天进程里多出一条 curl 外连请求,日志停在 __reduce__ 那一行。排查发现缓存值被人改写成了八十多字节的 payload。乍看像缓存或者权限的锅,追下去往往不是。

import json, redis
raw = r.get('sess:42')
data = json.loads(raw) if raw else {}

追下去是这么回事:pickle 在还原对象时会按数据里的指示去 import 模块并调用可调用对象,这个过程等于把执行权交给了数据源。缓存或者消息队列只要能被外部写入,就等于开放了一个远程执行入口。

碰到「pickle 反序列化」这类问题,我习惯先把当前配置原样备份一份,改坏了立刻换回来,不用现场推。接口问题先分清是没发出去、发出去了没收到,还是收到了处理失败。

具体做法是,外部来源的数据一律换成 JSON 或 MessagePack 这类纯数据结构;确实要缓存对象 就只存主键 取出来再查一次库;缓存服务加上认证 监听地址收到本机或内网;全局搜一遍 pickle.loads 和 yaml.load 把调用点列出来逐个改。

验收就看一点,往缓存里塞一个 __reduce__ payload 再读 进程不执行只抛 JSONDecodeError。

作者 9823189