如何审计 Python 服务中的不安全反序列化调用点?

1 人参与

在 Python 服务中,缓存、消息队列和数据库常被默认当作“内部可信边界”,于是读取端直接调用 pickle.loads 还原数据。问题在于,反序列化的风险取决于数据能否被不可信方影响,而不取决于存储介质是否部署在内网。只要攻击者能触及某条写入路径、队列消息或缓存内容,读取端在还原时就可能执行其中嵌入的调用。审计的起点,是把“谁能写入、谁会读取、数据是否经过可信校验”这三个问题问清楚。

pickle 并不只是把字节还原成字典和列表。它会按载荷指示查找并调用对象,因此处理不可信 pickle 可能导致任意代码执行。一个典型的脆弱调用点往往藏在封装里,例如从缓存取值后直接还原。关键不在这行是否使用了缓存,而在返回的数据能否被外部构造;换用 pickle.Unpickler 或自建包装函数,也不会自动消除这一点。

定位调用点并追踪数据流

审计应先在仓库中搜索反序列化入口,常见关键词包括 pickle.loads、pickle.load、pickle.Unpickler,以及 dill.loads、cloudpickle.loads,同时覆盖项目自建封装和依赖库中的路径。仅定位调用点并不足够,还需沿数据流核实载荷来自哪个生产者、哪些身份有写入权限、哪些消费者会读取,并特别检查任务重试、死信队列、缓存回填和历史数据回放,因为它们可能继续处理旧载荷。静态扫描可帮助发现可疑点,但不能单独证明安全或漏洞成立。验证时只在隔离环境中使用无害测试载荷,不要把真实生产数据复制过来后直接反序列化,任何无法确认安全的旧载荷都不应通过“试着加载一下”来检查。

用结构化契约替代执行能力

长期修复不是更换序列化语法,而是让数据只表达经过校验的业务字段。迁移到 JSON 或结构化消息时,应同时约束格式版本、必填字段、类型、允许值和大小,拒绝未知操作与超限载荷。需要警惕的是“先试 JSON、失败就回退 pickle”的兜底写法:攻击者可以利用解析失败把数据引向危险分支。给 pickle 载荷加签也不能将其变为安全格式,签名只能在正确验证后帮助确认来源,无法改变 pickle 具备执行能力这一事实。真正收敛风险的方向,是缩小写入权限、移除不可信 pickle 读取,并用输入校验和回归测试持续验证新边界。

参与讨论

1 条评论
  • 月隐星河

    内网可信这个假设坑过太多人

    回复