Technical Article

Paměťově bezpečná analýza PDF: Obrana proti škodlivým dokumentům

Dokumenty PDF jsou neuvěřitelně výkonné, ale tato síla s sebou nese inherentní bezpečnostní rizika. Vzhledem k tomu, že soubory PDF podporují vložené soubory, interaktivní JavaScript a složité binární streamy, často se používají jako vektory pro šíření malwaru. Přetečení vyrovnávací paměti, čtení mimo hranice a přetečení celých čísel ve špatně napsaných analyzátorech PDF mohou vést ke vzdálenému spuštění kódu (RCE).

Pokud v Delphi vytváříte aplikaci, která přijímá soubory PDF nahrané uživateli (např. portál pro příjem dokumentů), je zajištění paměťově bezpečné analýzy PDF kritickým bezpečnostním požadavkem.

Běžné vektory útoků na PDF

Škodlivá PDF se obvykle zaměřují na zranitelnosti v samotném analyzátoru, nikoli v operačním systému. Mezi běžné techniky patří:

  • Poškozené tabulky křížových odkazů (XRef): Vytváření posunů ukazatelů, které vedou mimo stanovené hranice, což způsobí pád analyzátoru nebo umožní odhalení obsahu paměti.
  • Nekonečné smyčky: Vytváření cyklických odkazů mezi objekty PDF (např. objekt A odkazuje na objekt B, který odkazuje zpět na objekt A), což vede k vyčerpání zásobníku.
  • Explodující dekomprese (Zip Bombs): Streamy FlateDecode, které se dekomprimují z několika kilobajtů na gigabajty a vyčerpají tak systémovou paměť.

Strategie defenzivní analýzy v Delphi

Při nativní analýze PDF v Delphi musíte programovat defenzivně. Nemůžete důvěřovat metadatům poskytnutým v PDF slovnících.

1. Přerušení cyklických odkazů

Při rekurzivním procházení stromu objektů PDF musíte udržovat historii navštívených objektů, abyste zabránili nekonečným smyčkám.

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. Ochrana proti Zip bombám

Při použití filtru FlateDecode pro dekompresi streamu musíte striktně omezit maximální velikost expanze. Nikdy nealokujte paměť slepě na základě klíče `/Length` ve slovníku.

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;

Využití posílených enginů a bezpečných komponent

Napsat zcela bezpečný PDF parser od nuly je monumentální úkol. Standardním průmyslovým přístupem je použití posíleného enginu, který je silně testován metodou fuzzing, jako je PDFium, nebo spoléhání na přísně testované nativní knihovny.

PDFium je hlavní renderovací engine používaný prohlížečem Google Chrome. Protože Chrome denně zpracovává miliony nedůvěryhodných PDF, je PDFium vystaveno agresivnímu a neustálému fuzzingu od týmu Project Zero společnosti Google. Elegantně si poradí s poškozenými XRef tabulkami, rozbitými streamy a cyklickými odkazy.

Podobně i nativní komponenty jako HotPDF Component a Delphi PDF Library obsahují robustní defenzivní parsovací strategie již v základu. Implementují přísnou kontrolu mezí, omezovače hloubky rekurze a mechanismy pro prevenci úniku paměti navržené speciálně pro prostředí Delphi a C++Builder.

Ať už se rozhodnete konzumovat PDFium přes Delphi wrapper pro renderování, nebo využijete nativní komponenty jako HotPDF pro generování a zpracování dokumentů, zdědíte bezpečnostní perimetr na podnikové úrovni, který ochrání vaše uživatele a servery před škodlivým obsahem, aniž byste museli sami psát defenzivní parsery.

Poznámka: Bezpečné parsovací schopnosti prověřené fuzzingem jsou dostupné napříč celou naší sadou, včetně HotPDF Component, Delphi PDF Library a PDFium Component.