Technical Article

Bezbedno parsiranje PDF-a po memoriju: Odbrana od zlonamernih dokumenata

PDF dokumenti su neverovatno moćni, ali ta moć nosi inherentne bezbednosne rizike. Zato što PDF-ovi podržavaju ugrađene datoteke, interaktivni JavaScript i složene binarne tokove, često se koriste kao vektori za isporuku zlonamernog softvera. Prelivanja bafera, čitanja van granica i prekoračenja celih brojeva u loše napisanim PDF parserima mogu dovesti do daljinskog izvršavanja koda (RCE).

Ako gradite aplikaciju u Delphiju koja prihvata PDF-ove koje otpremaju korisnici (npr. portal za unos dokumenata), obezbeđivanje memorijski bezbednog parsiranja PDF-a je kritičan bezbednosni zahtev.

Uobičajeni vektori PDF napada

Zlonamerni PDF-ovi obično ciljaju ranjivosti u samom parseru umesto u operativnom sistemu. Uobičajene tehnike uključuju:

  • Loše formatirane tabele unakrsnih referenci (XRef): Kreiranje ofseta pokazivača koji vode van granica, uzrokujući pad parsera ili omogućavajući otkrivanje memorije.
  • Beskonačne petlje: Stvaranje kružnih referenci između PDF objekata (npr. Objekat A referencira Objekat B, koji referencira Objekat A) što dovodi do iscrpljivanja steka.
  • Eksplozivna dekompresija (Zip Bombe): Tokovi FlateDecode koji se dekompresuju od nekoliko kilobajta u gigabajte, iscrpljujući sistemsku memoriju.

Defanzivne strategije parsiranja u Delphiju

Kada izvorno parsirate PDF-ove u Delphiju, morate programirati defanzivno. Ne možete verovati metapodacima koji su navedeni u PDF rečnicima.

1. Razbijanje kružnih referenci

Kada rekurzivno prolazite kroz stablo PDF objekata, morate održavati istoriju posećenih objekata da biste sprečili beskonačne petlje.

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štita od Zip bombi

Kada primenjujete filter FlateDecode za dekompresiju toka, morate strogo ograničiti maksimalnu veličinu ekspanzije. Nikada ne alocirajte memoriju na slepo na osnovu ključa rečnika /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;

Korišćenje ojačanih mehanizama i bezbednih komponenti

Pisanje potpuno bezbednog PDF parsera od nule je monumentalan zadatak. Standardni pristup u industriji je korišćenje ojačanog, snažno fuzz-testiranog mehanizma kao što je PDFium, ili oslanjanje na rigorozno testirane nativne biblioteke.

PDFium je osnovni mehanizam za renderovanje koji koristi Google Chrome. Pošto Chrome svakodnevno obrađuje milione nepouzdanih PDF-ova, PDFium je podvrgnut agresivnom, kontinuiranom fuzzingu od strane Google-ovog Project Zero tima. On se elegantno nosi sa neispravno oblikovanim XRef-ovima, prekinutim tokovima i cikličnim referencama.

Slično tome, nativne komponente poput HotPDF Component i Delphi PDF Library unapred uključuju robusne strategije odbrambenog parsiranja. One implementiraju strogu proveru granica, graničnike rekurzivne dubine i mehanizme za sprečavanje curenja memorije dizajnirane specifično za Delphi i C++Builder okruženja.

Bilo da izaberete da koristite PDFium putem Delphi omotača (wrappera) za renderovanje ili da koristite nativne komponente poput HotPDF-a za generisanje i obradu dokumenata, nasleđujete bezbednosni perimetar na nivou preduzeća, štiteći svoje korisnike i servere od zlonamernih opterećenja bez potrebe da sami pišete odbrambene parsere.

Napomena: Bezbedne mogućnosti parsiranja testirane fuzzingom dostupne su u celom našem paketu, uključujući HotPDF Component, Delphi PDF Library i PDFium Component.