PDF 檔案可以做很多事情,而這種彈性也伴隨著固有的安全風險。因為 PDF 支援內嵌檔案、互動式 JavaScript 以及複雜的二進位串流,它們經常被用作傳遞惡意軟體的媒介。編寫不良的 PDF 解析器中的緩衝區溢位、越界讀取和整數溢位,都可能導致遠端程式碼執行(RCE)
如果您正在建置一個接受使用者上傳 PDF(例如檔案攝取入口網站)的 Delphi 應用程式,確保記憶體安全的 PDF 解析是一項至關重要的安全需求
常見的 PDF 攻擊媒介
惡意 PDF 通常會針對解析器本身的漏洞,而非作業系統。常見的技術包含:
- 格式錯誤的交互參照(XRef)表: 製作會導致越界指標偏移,使解析器崩潰或允許記憶體洩漏
- 無窮迴圈: 在 PDF 物件之間建立循環參照(例如,物件 A 參照物件 B,而物件 B 又參照物件 A),導致堆疊耗盡
- 爆炸性解壓縮(Zip 炸彈):
FlateDecode串流從幾 KB 解壓縮為數 GB,耗盡系統記憶體
Delphi 中的防禦性解析策略
在 Delphi 中原生解析 PDF 時,您必須採取防禦性的程式設計。您不能信任 PDF 字典中提供的詮釋資料(metadata)
1. 打破循環參照
在遞迴走訪 PDF 物件樹時,您必須維護已走訪物件的歷史記錄,以防止無窮迴圈
uses
System.Generics.Collections, System.SysUtils;
// 一個安全走訪 PDF 樹的遞迴函式
procedure ParsePDFDictionary(DictObj: TPDFDictionary; Visited: TList<Integer>);
var
ObjID: Integer;
begin
ObjID := DictObj.ObjectID;
if Visited.Contains(ObjID) then
begin
Writeln('警告:偵測到循環參照。中止分支。');
Exit;
end;
Visited.Add(ObjID);
try
// 安全地處理子物件...
finally
// 允許同層物件走訪,但防止垂直的遞迴迴圈
Visited.Remove(ObjID);
end;
end;
2. 防禦 Zip 炸彈
當套用 FlateDecode 篩選器來解壓縮串流時,您必須嚴格限制最大擴張大小。絕不要盲目地根據 /Length 字典鍵來配置記憶體
const
MAX_DECOMPRESSED_SIZE = 1024 * 1024 * 50; // 50 MB 安全限制
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('安全性例外:偵測到解壓縮炸彈!');
OutputTarget.WriteBuffer(Buffer[0], BytesRead);
end;
until BytesRead = 0;
finally
ZLibStream.Free;
end;
end;
善用強化的引擎與安全的元件
從頭開始撰寫一個完全安全的 PDF 解析器是一項艱鉅的任務。業界標準的作法是使用一個經過強化且經過大量模糊測試(fuzz-tested)的引擎,如 PDFium,或者是依賴經過嚴格測試的原生程式庫
PDFium 是 Google Chrome 使用的核心渲染引擎。因為 Chrome 每天處理數以百萬計不受信任的 PDF,PDFium 受到 OSS-Fuzz 與 ClusterFuzz 持續且猛烈的模糊測試。它可以優雅地處理格式錯誤的 XRef、損壞的串流以及循環參照
同理,原生的元件如 HotPDF 元件 與 Delphi PDF 庫 開箱即結合了經過充分測試的防禦性解析策略。它們實作了嚴格的邊界檢查、遞迴深度限制器以及記憶體洩漏預防機制,這些都是專為 Delphi 與 C++Builder 環境所設計的
無論您選擇透過 Delphi 封裝程式使用 PDFium 進行渲染,或是利用如 HotPDF 等原生元件進行檔案產生與處理,您都繼承了企業級的安全邊界,可以保護您的使用者和伺服器免於惡意酬載的攻擊,而不需要您自己編寫防禦性的解析器
注意:安全且經過模糊測試的解析功能在我們的整個套件中都可使用,包含 HotPDF 元件、Delphi PDF 庫 以及 PDFium 元件