Technical Article

Pomnilniško varno razčlenjevanje PDF: Obramba pred zlonamernimi dokumenti

Dokumenti PDF so neverjetno zmogljivi, vendar pa ta zmogljivost prinaša tudi inherentna varnostna tveganja. Ker datoteke PDF podpirajo vdelane datoteke, interaktivni JavaScript in kompleksne binarne tokove, se pogosto uporabljajo kot vektorji za dostavo zlonamerne programske opreme. Prekoračitve medpomnilnika (buffer overflows), branja izven meja (out-of-bounds reads) in prekoračitve celih števil (integer overflows) v slabo napisanih razčlenjevalnikih PDF lahko vodijo do oddaljenega izvajanja kode (RCE).

Če v Delphiju gradite aplikacijo, ki sprejema PDF datoteke, ki jih naložijo uporabniki (npr. portal za sprejem dokumentov), je zagotavljanje pomnilniško varnega razčlenjevanja PDF kritična varnostna zahteva.

Pogosti vektorji napadov prek PDF

Zlonamerne PDF datoteke običajno ciljajo na ranljivosti v samem razčlenjevalniku (parser) in ne v operacijskem sistemu. Pogoste tehnike vključujejo:

  • Nepravilno oblikovane tabele navzkrižnih referenc (XRef): Izdelava zamikov kazalcev (pointer offsets), ki vodijo izven meja, kar povzroči sesutje razčlenjevalnika ali omogoči razkritje pomnilnika.
  • Neskončne zanke: Ustvarjanje krožnih referenc med objekti PDF (npr. objekt A se sklicuje na objekt B, ta pa se sklicuje na objekt A), kar vodi do izčrpanosti sklada (stack exhaustion).
  • Eksplozivna dekompresija (Zip Bombs): Tokovi z aplikacijo FlateDecode, ki se iz nekaj kilobajtov dekompresirajo v gigabajte, s čimer izčrpajo sistemski pomnilnik.

Strategije defenzivnega razčlenjevanja v Delphiju

Pri izvornem razčlenjevanju datotek PDF v Delphiju morate programirati defenzivno. Metapodatkom, ki so podani v slovarjih PDF, ne smete zaupati.

1. Prekinitev krožnih referenc

Pri rekurzivnem sprehajanju po drevesu objektov PDF morate ohraniti zgodovino obiskanih objektov, da preprečite neskončne zanke.

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. Zaščita pred datotekami tipa Zip Bomb

Ko uporabite filter FlateDecode za dekompresijo toka, morate strogo omejiti največjo dovoljeno velikost razširitve. Nikoli ne dodeljujte pomnilnika slepo na podlagi ključa v slovarju `/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;

Izkoriščanje utrjenih mehanizmov in varnih komponent

Pisanje popolnoma varnega razčlenjevalnika PDF od začetka je monumentalna naloga. Standardni pristop v industriji je uporaba utrjenega, močno fuzz-testiranega mehanizma, kot je PDFium, ali zanašanje na strogo preizkušene izvorne knjižnice.

PDFium je osnovni mehanizem za upodabljanje, ki ga uporablja Google Chrome. Ker Chrome dnevno obdela milijone nezanesljivih datotek PDF, je PDFium podvržen agresivnemu in nenehnemu testiranju fuzzing s strani Googlove skupine Project Zero. Elegantno obravnava napačno oblikovane XRef-e, prekinjene tokove in ciklične reference.

Podobno izvorne komponente, kot sta HotPDF Component in Delphi PDF Library, že privzeto vključujejo robustne obrambne strategije razčlenjevanja. Implementirajo strogo preverjanje meja, omejevalnike rekurzivne globine in mehanizme za preprečevanje uhajanja pomnilnika, zasnovane posebej za okolji Delphi in C++Builder.

Ne glede na to, ali se odločite za uporabo PDFium prek ovoja Delphi za upodabljanje ali uporabite izvorne komponente, kot je HotPDF, za generiranje in obdelavo dokumentov, podedujete varnostni obod na ravni podjetja, ki ščiti vaše uporabnike in strežnike pred zlonamernimi bremeni, ne da bi vam bilo treba samim pisati obrambne razčlenjevalnike.

Opomba: Varne, fuzz-testirane zmožnosti razčlenjevanja so na voljo v celotnem našem paketu, vključno s HotPDF Component, Delphi PDF Library in PDFium Component.