夜雨聆风学习资料网

ARTICLE · 1349642

40个字节的PDF,直接让进程罢工

40个字节的PDF,直接让进程罢工

PDF 解析失败会报错?

40 个字节的 PDF,

让进程再也不返回

七个 CVE · 三版本对照 · 一份可直接抄的超时封装

pypdf 实测复盘

pypdfDoS

📦 5 Parts + Conclusion

👉 滑动

PART 01

四十个字节

the file

PART 02

超时无效

timeout

PART 03

补丁时间线

patches

PART 04

升不升 7.0

upgrade

PART 05

五个坑

pitfalls

PART ///

写在最后

收尾

pypdf 七天发了三个补丁,7.0.0 于 11 月 6 日发布

它卡死的时候,不报错也不返回

01

PART

一份 40 字节的文件

THE FILE

pypdf 是 Python 里用得最多的 PDF 库之一,拆页、合并、抽文本基本绕不开。10 月 17 日到 11 月 6 日,它连发五个版本,其中 21 日、27 日、28 日三天各发一个,最后跳到 7.0.0。五个版本一共 13 条安全改动,7 条对应已公开的 CVE,级别全是高危。

这七条的共同点不是远程执行,是拒绝服务:喂它一份构造过的 PDF,解析过程会失控,CPU 打满、函数不返回、日志里一个字都没有。进程没崩,只是从此不再响应任何请求。

最小的一份复现只有 40 字节。pypdf 自己把它写进了回归测试,就在 6.21.4 修 xref 死循环的那次提交里(#4289)。把这七行存成 40b.pdf:

...text

%PDF-1.4

startxref

28

%%EOF

xref

1 0

aaa

读它只需要一行:

...python

from pypdf import PdfReader

PdfReader("40b.pdf") # 6.21.2 上不返回

结构值得看一眼。startxref 指向偏移 28,那里是 xref 关键字;紧接着本该是条目数和条目表,实际只有「1 0」和三个字母。解析器读完这一行读不到 trailer 关键字,就把游标往回退 7 字节再读一次,退到的还是同一处,于是在同一段字节上无限打转。

三个版本跑同一份文件,结果分成两档:

版本
结果
6.21.2
8 秒未返回,被强杀
6.21.4
立即抛 PdfReadError
7.0.0
立即抛 PdfReadError

报错原文是 Xref parsing stuck at position 33,位置正好落在那三个字母上。同一份文件在 6.21.2 上,我让一个守候线程在第 3 秒采样:墙钟走了 3.0 秒,进程 CPU 时间已经 3.45 秒。也就是说这段时间它一直在算,一秒都没在等 IO。内存告警不会响,慢查询不会响,只有接口超时。

02

PART

这类故障为什么杀不掉

TIMEOUT

第一反应一定是加超时。三个位置我都试了,只有最后一个管用。

线程超时

Python 没有中断线程的接口,Thread.join(timeout) 到点只是不再等它,线程本身还在跑。

信号闹钟

signal.alarm 只有 Unix 才有,而且必须在主线程里调用,Web 框架的业务代码基本都在别的线程。

业务层设上限

限制页数、限制体积都拦不住,死循环发生在构造阶段,你的限制拿到的对象还没有 .pages。

真正管用的只有进程边界。把解析放进子进程,超时就杀掉,进程一死,CPU 和内存一并还回去:

...python

import subprocess, sys

CODE = ("import pypdf,sys;"

    "pypdf.PdfReader(sys.argv[1])")

PY = sys.executable

def parsed(path, timeout=5):

  try:

    out = subprocess.run(

      [PY, "-c", CODE, path],

      capture_output=True,

      timeout=timeout)

  except subprocess.TimeoutExpired:

    return False

  return out.returncode == 0

返回 False 就是这份文件不要了;超时那一支里,子进程已经被终止。这个 timeout 是硬终止:POSIX 下发 SIGKILL,Windows 下走 TerminateProcess,两条路都不会被死循环挡住。代价是每份文件多一次解释器冷启动,几十毫秒量级;批处理场景换成常驻进程池,超时后 terminate 再补一个新 worker 更划算。

03

PART

七天三个补丁,修的是同一面墙

PATCHES

把四个版本的 changelog 摊开看,安全段落的节奏是这样的:

版本
关掉的入口
6.21.1
10-17 · JPEG 解码只走 Pillow 插件
6.21.2
10-21 · xref 表长度上限、避免整文件重复加载
6.21.3
10-27 · 交叉引用流条目总数、解密后页数、异常信息收敛
6.21.4
10-28 · EOF 处 trailer 搜索、字典克隆环检测、页属性上限

七个 CVE 全部落在最后两版:6.21.3 关掉四条(页标签、严格模式异常、加密文件、交叉引用流),6.21.4 关掉三条(EOF 处的 trailer 搜索、页树、字典克隆)。全都是高危,全都是解析耗时或内存随输入长度失控。

有个细节值得记一笔:页标签那条 CVE-2026-119206,对应的修复在 6.21.3 的 changelog 里排在 Performance Improvements 分类下(#4251,所有页只读一遍标签号树)。作者提交时它看起来就是一次性能优化。按分类去数公告条数,会少算一条。

还有一点反直觉:七条全是拒绝服务,没有一条是远程执行或越权。所以「我们只在内网用」不构成理由,只要有上传口,攻击面就在上传口。

04

PART

要不要马上换 7.0.0

UPGRADE

7.0.0 于 11 月 6 日发布,主版本号跳了一格,按 pypdf 的惯例意味着会删东西。我把它和 6.21.4 装在两个独立环境里逐项对跑,会当场报错的有三处:

删掉的
替代
add_js
add_open_action
decode_permissions
user_access_permissions
filters.CCITParameters
filters.CCITTParameters

这三个名字在 6.21.4 上都还能用,只是带弃用警告。第一项抛的 DeprecationError 原文写着 removed in pypdf 7.0.0,新方法要传一个 JavaScript 对象而不是字符串;第三项只差一个字母。本地跑得好好的脚本,升上去当场断,这是主版本号最容易伤到人的地方。

环境层面还有两条门槛:requires_python 从这一版起是 >=3.10,3.9 装不上;[crypto] 这个额外依赖里的 cryptography 下限提到 43,注意它只是 extra,不装不受影响,装了就得一起升。

现状
建议
只解析,不加密
直接升 7.0.0,多拿 4 条未编号的硬化
自己的脚本用了 add_js
先钉 pypdf>=6.21.4,<7,改完再升
还要求 Python 3.9
停在 6.21.4,7 条公开 CVE 已全部关掉

7.0.0 那 4 条硬化没有 CVE 编号,方向是一致的:对象流里偏移必须严格递增、XFA 表单解码工作量、图片尺寸上限、文件标识符改成随机。前三条还是抗 DoS。最后一条影响面比听起来小:不加密的 PdfWriter 输出里根本没有 /ID 字段,两次写出结果一致;只有走加密路径才会带上随机标识,这时同一份内容两次输出的字节不再相同,拿输出哈希做去重或缓存键的会全量 miss。

05

PART

几个容易搞错方向的地方

PITFALLS

两个包并存

pip list 里 PyPDF2 和 pypdf 常常同时存在,import pypdf 拿到的究竟是哪个,得自己确认。

strict 不是开关

它默认就是 False,我两种取值都跑过,这份 40 字节一样死循环。

大小不是防线

这份是 40 字节;换成 xref 表里声明上百万个条目,文件不大也能让它跑很久,体积检查只挡得住最笨的那一类。

升完记得重启

pip install -U 只改磁盘上的包,长驻进程里的模块还在内存里,不重启就还在跑旧代码。

它不负责安全检测

pypdf 保证的是自己不在解析时失控,至于渲染、OCR、文件里藏了什么,都不是它的事。

///

LAST

写在最后

WRAP UP

解析别人给的文件,是所有日常操作里最该按「会失败」来设计的一类。本地的 Excel 坏了你看得见,上传上来的 PDF 坏了,只会让进程安静地转圈。

所以两件事要分开做:已知的七个入口靠升级关掉,未知的靠进程边界兜住。前者是改一行版本号的事,后者是改一次架构的事。只做前一件,下次公告出来你还得再来一遍。

已知的靠升级,未知的靠超时。一份 40 字节的文件能教的东西,比一份安全公告多。

如果这篇内容对你有帮助,欢迎点赞、在看、转发三连,我们下篇见。

既然看到这里了,如果觉得有用,随手点个赞、在看、转发三连吧。

点赞
在看
转发

THANKS FOR READING

回首页,搜索你想要的文档

在首页搜索框输入关键词,快速定位资料

文库首页搜索指引 立即搜索

相关学习资料