Technical Article

記憶體安全的 PDF 解析:防禦惡意文件

PDF 文件非常強大,但這種強大伴隨著固有的安全風險。由於 PDF 支援內嵌檔案、互動式 JavaScript 與複雜的二進位串流,它們經常被用作傳遞惡意軟體的媒介。在寫得不好的 PDF 解析器中,緩衝區溢位、超出邊界讀取和整數溢位可能導致遠端執行程式碼 (RCE)。

如果您正在 Delphi 中建置接受使用者上傳 PDF 的應用程式 (例如文件接收入口網站),確保記憶體安全的 PDF 解析是一項關鍵的安全要求。

常見的 PDF 攻擊載體

惡意的 PDF 通常以解析器本身的漏洞為目標,而不是作業系統。常見技術包括:

  • 格式不良的交叉參照 (XRef) 表格:製作超出邊界的指標偏移量,使解析器當機或允許記憶體洩漏。
  • 無限迴圈:在 PDF 物件之間建立循環參照 (例如:物件 A 參照物件 B,物件 B 參照物件 A),導致堆疊耗盡。
  • 爆炸式解壓縮 (Zip 炸彈):從幾 KB 解壓縮成數 GB 的 FlateDecode 串流,耗盡系統記憶體。

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 ComponentDelphi PDF Library 等原生元件在預設情況下即整合了強大的防禦性解析策略。它們實作了專門針對 Delphi 與 C++Builder 環境設計的嚴格邊界檢查、遞迴深度限制器以及記憶體洩漏預防機制。

無論您是選擇透過 Delphi 包裝函式庫呼叫 PDFium 進行彩現,還是利用 HotPDF 等原生元件建立與處理文件,您都能獲得企業級的安全性邊界,保護您的使用者與伺服器免受惡意負載的攻擊,而無需自行撰寫防禦性解析器。

注意:安全、經過模糊測試的解析能力支援我們的整個產品套件,包含 HotPDF ComponentDelphi PDF Library 以及 PDFium Component