PDF 文档功能极其强大,但这种强大伴随着固有的安全风险。由于 PDF 支持嵌入式文件、交互式 JavaScript 和复杂的二进制流,它们经常被用作传输恶意软件的载体。编写糟糕的 PDF 解析器中出现的缓冲区溢出、越界读取和整数溢出可能导致远程代码执行(RCE)。
如果您正在 Delphi 中构建一个接受用户上传的 PDF 的应用程序(例如,文档接收门户),确保内存安全的 PDF 解析是一项关键的安全要求。
常见的 PDF 攻击载体
恶意的 PDF 通常针对解析器本身的漏洞而不是操作系统。常见的技术包括:
- 格式不良的交叉引用(XRef)表: 伪造导致越界的指针偏移,使解析器崩溃或允许内存泄漏。
- 无限循环: 在 PDF 对象之间创建循环引用(例如,对象 A 引用对象 B,对象 B 又引用对象 A),从而导致堆栈耗尽。
- 爆炸式解压(Zip 炸弹):
FlateDecode流从几 KB 解压缩到数 GB,耗尽系统内存。
Delphi 中的防御性解析策略
在 Delphi 中原生地解析 PDF 时,您必须进行防御性编程。您不能信任 PDF 字典中提供的元数据。
1. 打破循环引用
在递归遍历 PDF 对象树时,您必须维护已访问对象的历史记录以防止无限循环。
uses
System.Generics.Collections, System.SysUtils;
// A safe recursive function to walk the PDF tree
procedure ParsePDFDictionary(DictObj: TPDFDictionary; Visited: TList<Integer>);
var
ObjID: Integer;
begin
ObjID := DictObj.ObjectID;
if Visited.Contains(ObjID) then
begin
Writeln('Warning: Circular reference detected. Aborting branch.');
Exit;
end;
Visited.Add(ObjID);
try
// Process child objects safely...
finally
// Allow siblings to traverse, but prevent vertical recursion loops
Visited.Remove(ObjID);
end;
end;
2. 防御 Zip 炸弹
应用 FlateDecode 过滤器对流进行解压时,您必须严格限制最大解压尺寸。切勿盲目地根据 /Length 字典键分配内存。
const
MAX_DECOMPRESSED_SIZE = 1024 * 1024 * 50; // 50 MB safety limit
procedure DecompressPDFStream(CompressedStream, OutputTarget: TStream);
var
ZLibStream: TZDecompressionStream;
Buffer: array[0..8191] of Byte;
BytesRead, TotalRead: Integer;
begin
ZLibStream := TZDecompressionStream.Create(CompressedStream);
try
TotalRead := 0;
repeat
BytesRead := ZLibStream.Read(Buffer[0], SizeOf(Buffer));
if BytesRead > 0 then
begin
TotalRead := TotalRead + BytesRead;
if TotalRead > MAX_DECOMPRESSED_SIZE then
raise Exception.Create('Security Exception: Decompression bomb detected!');
OutputTarget.WriteBuffer(Buffer[0], BytesRead);
end;
until BytesRead = 0;
finally
ZLibStream.Free;
end;
end;
利用加固引擎与安全组件
从头编写一个完全安全的 PDF 解析器是一项浩大的工程。行业标准的方法是使用经过深度模糊测试(fuzz-tested)的加固引擎,如 PDFium,或依赖经过严格测试的原生库。
PDFium 是 Google Chrome 使用的核心渲染引擎。由于 Chrome 每天处理数以百万计的不可信 PDF,PDFium 受到 Google Project Zero 持续而猛烈的模糊测试。它能够从容地处理格式错误的 XRef、损坏的流和循环引用。
同样,HotPDF Component 和 Delphi PDF Library 等原生组件开箱即用地集成了强大的防御性解析策略。它们实现了专门针对 Delphi 和 C++Builder 环境设计的严格边界检查、递归深度限制器以及内存泄漏预防机制。
无论您是选择通过 Delphi 包装器调用 PDFium 进行渲染,还是利用 HotPDF 等原生组件生成和处理文档,您都能获得企业级的安全边界,保护您的用户和服务器免受恶意负载的攻击,而无需自己编写防御性解析器。
注意:安全、经过模糊测试的解析能力贯穿我们的整个产品套件,包括 HotPDF Component、Delphi PDF Library 以及 PDFium Component。