Technical Article

Sauganti atmintį PDF analizė: gynyba nuo kenkėjiškų dokumentų

PDF dokumentai yra nepaprastai galingi, tačiau ši galia atneša būdingą saugumo riziką. Kadangi PDF failai palaiko įterptuosius failus, interaktyvų „JavaScript“ ir sudėtingus dvejetainius srautus, jie dažnai naudojami kaip kenkėjiškų programų platinimo vektoriai. Buferio perpildymai, skaitymai už ribų (angl. out-of-bounds reads) ir sveikųjų skaičių perpildymai prastai parašytuose PDF analizatoriuose gali lemti nuotolinį kodo vykdymą (angl. RCE).

Jei kuriate programą „Delphi“ aplinkoje, kuri priima vartotojų įkeltus PDF failus (pvz., dokumentų priėmimo portalą), atmintį saugančios (angl. memory-safe) PDF analizės užtikrinimas yra kritinis saugumo reikalavimas.

Dažni PDF atakų vektoriai

Kenkėjiški PDF failai paprastai taikosi į pažeidžiamumus pačiame analizatoriuje, o ne operacinėje sistemoje. Dažni metodai yra šie:

  • Netinkamai suformuotos kryžminių nuorodų („XRef“) lentelės: rodyklių (angl. pointers) poslinkių konstravimas taip, kad jie išeitų už ribų, taip užlaužiant analizatorių arba leidžiant nutekinti atmintį.
  • Begalinis ciklas: žiedinių nuorodų kūrimas tarp PDF objektų (pvz., objektas A nurodo į objektą B, kuris nurodo atgal į objektą A), dėl kurio išseikvojamas stekas.
  • Sprogstantis dekompresavimas (užtrauktuko bombos, angl. Zip Bombs): FlateDecode srautai, kurie iš kelių kilobaitų išskleidžiami į gigabaitus, taip išsekinant sistemos atmintį.

Gynybinės analizės strategijos „Delphi“ aplinkoje

Analizuodami PDF natyviai „Delphi“ aplinkoje, privalote programuoti gynybiškai. Negalite pasitikėti metaduomenimis, pateiktais PDF žodynuose.

1. Žiedinių nuorodų nutraukimas

Rekursyviai eidami per PDF objektų medį, privalote išlaikyti aplankytų objektų istoriją, kad išvengtumėte begalinių ciklų.

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. Apsauga nuo „Zip bombų“

Kai taikote FlateDecode filtrą srauto išskleidimui, privalote griežtai apriboti maksimalų išplėtimo dydį. Niekada nepriskirkite atminties aklai, remdamiesi /Length žodyno raktu.

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;

Sustiprintų variklių ir saugių komponentų panaudojimas

Visiškai saugaus PDF analizatoriaus rašymas nuo nulio yra monumentalus darbas. Standartinis pramonės požiūris yra naudoti sustiprintą, intensyviai fuzzingu testuotą variklį, pvz., PDFium, arba pasikliauti griežtai išbandytomis vietinėmis (native) bibliotekomis.

PDFium yra pagrindinis atvaizdavimo variklis, kurį naudoja „Google Chrome“. Kadangi „Chrome“ kasdien apdoroja milijonus nepatikimų PDF failų, „Google Project Zero“ PDFium nuolat ir agresyviai testuoja fuzzingu. Jis sklandžiai apdoroja netinkamai suformuotus XRef, sugadintus srautus ir ciklines nuorodas.

Panašiai, vietiniuose komponentuose, tokiuose kaip HotPDF Component ir Delphi PDF Library, standartiškai integruotos patikimos gynybinės analizės strategijos. Juose įdiegtas griežtas ribų tikrinimas, rekursyvaus gylio ribotuvai ir atminties nutekėjimo prevencijos mechanizmai, sukurti specialiai Delphi ir C++Builder aplinkoms.

Nesvarbu, ar pasirinksite naudoti PDFium per Delphi „wrapper“ atvaizdavimui, ar naudosite vietinius komponentus, tokius kaip HotPDF, dokumentų generavimui ir apdorojimui, jūs paveldėsite įmonės lygio saugos perimetrą, apsaugantį jūsų vartotojus ir serverius nuo kenkėjiškų naudingų apkrovų, jums patiems nereikės rašyti gynybinių analizatorių.

Pastaba: Saugios, fuzzingu testuotos analizės galimybės prieinamos visame mūsų rinkinyje, įskaitant HotPDF Component, Delphi PDF Library ir PDFium Component.