Technical Article

Memorijski sigurno parsiranje PDF-a: Obrana od zlonamjernih dokumenata

PDF dokumenti su nevjerojatno moćni, ali ta moć donosi inherentne sigurnosne rizike. Budući da PDF-ovi podržavaju ugrađene datoteke, interaktivni JavaScript i složene binarne tokove, često se koriste kao vektori za isporuku zlonamjernog softvera. Prelijevanje međuspremnika, čitanja izvan granica i prelijevanja cijelih brojeva (integer overflows) u loše napisanim PDF parserima mogu dovesti do daljinskog izvršavanja koda (RCE).

Ako gradite aplikaciju u Delphiju koja prihvaća PDF-ove prenesene od strane korisnika (npr. portal za unos dokumenata), osiguranje memorijski sigurnog parsiranja PDF-a ključni je sigurnosni zahtjev.

Uobičajeni vektori PDF napada

Zlonamjerni PDF-ovi obično ciljaju na ranjivosti u samom parseru, a ne u operativnom sustavu. Uobičajene tehnike uključuju:

  • Loše oblikovane tablice unakrsnih referenci (XRef): Stvaranje pomaka pokazivača koji vode izvan granica, što uzrokuje pad parsera ili omogućuje otkrivanje memorije.
  • Beskonačne petlje: Stvaranje kružnih referenci između PDF objekata (npr. objekt A referencira objekt B, koji referencira objekt A) što dovodi do iscrpljivanja stoga.
  • Eksplozivna dekompresija (Zip bombe): FlateDecode tokovi koji se dekomprimiraju s nekoliko kilobajta u gigabajte, iscrpljujući sistemsku memoriju.

Strategije defanzivnog parsiranja u Delphiju

Prilikom izvornog parsiranja PDF-ova u Delphiju, morate programirati defanzivno. Ne možete vjerovati metapodacima navedenima u PDF rječnicima.

1. Prekidanje kružnih referenci

Kada rekurzivno prolazite kroz stablo PDF objekata, morate održavati povijest posjećenih objekata kako biste spriječ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 primjenjujete filtar FlateDecode za dekompresiju toka, morate strogo ograničiti maksimalnu veličinu proširenja. Nikada nemojte slijepo dodjeljivati memoriju na temelju tipke rječ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štenje ojačanih mehanizama i sigurnih komponenti

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

PDFium je osnovni mehanizam za iscrtavanje koji koristi Google Chrome. Budući da Chrome svakodnevno obrađuje milijune nepouzdanih PDF-ova, PDFium je podvrgnut agresivnom, kontinuiranom fuzzingu od strane Googleovog Project Zero tima. On se elegantno nosi s neispravno oblikovanim XRef-ovima, prekinutim tokovima i cikličkim referencama.

Slično tome, nativne komponente poput HotPDF Component i Delphi PDF Library unaprijed uključuju robusne strategije obrambenog parsiranja. One implementiraju strogu provjeru granica, ograničivače rekurzivne dubine i mehanizme za sprječavanje curenja memorije dizajnirane specifično za Delphi i C++Builder okruženja.

Bez obzira odaberete li konzumirati PDFium putem Delphi omotača (wrappera) za iscrtavanje ili koristiti nativne komponente poput HotPDF-a za generiranje i obradu dokumenata, nasljeđujete sigurnosni perimetar na razini poduzeća, štiteći svoje korisnike i servere od zlonamjernih opterećenja bez potrebe da sami pišete obrambene parsere.

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