Technical Article

Audit bezpečnostných rizík PDF s PDFium v Delphi

PDF nie je len papier. Je to kontajner, ktorý môže niesť skripty spúšťané pri otvorení súboru, odkazy spúšťajúce externé programy, odkazy kontaktujúce webové servery, súbory vnorené do iných súborov a podpis, ktorý tvrdí, že dokument sa nezmenil odkedy sa zaň niekto zaručil. Keď súbor príde zo zdroja, ktorý nemáte pod kontrolou, najbezpečnejším prvým krokom nie je jeho vykreslenie. Je ním prečítanie toho, čo súbor hovorí sám o sebe, a zostavenie zoznamu všetkého, o čo by sa mohol pokúsiť, aby človek mohol rozhodnúť, či vôbec patrí do vášho pracovného postupu.

Tento článok prechádza statickým auditom tohto rizikového povrchu len na čítanie pomocou komponentu PDFium pre Delphi a Lazarus. Audit nikdy nevykresľuje stránku. Analyzuje štruktúru dokumentu, spočítava časti súboru, ktoré nesú správanie, a zapisuje čistú správu. Je to rozdiel medzi tým, keď požiadate cudzinca pri dverách, aby vyprázdnil vrecká, a tým, keď mu dôverujete, pretože sa usmial.

Čo audit je a čo nie je

Majte jasno v hraniciach. Náhľad v sandboxe vykresľuje súbor pod prísnymi obmedzeniami, takže si ho používateľ môže prezrieť bez toho, aby sa súbor dotkol zvyšku počítača. Audit prichádza pred tým. Ide o kontrolu bez vykresľovania, ktorej jediným výstupom je popis povrchu hrozby: ktoré skripty existujú, ktoré akcie sú prepojené s odkazmi, či je súbor podpísaný a ako prísne, a čo je k nemu pripojené. Spúšťate ho, keď dokument prekročí hranicu dôvery, pri prijímaní z e-mailu, odosielacieho formulára alebo partnerského kanála, predtým, ako ho akákoľvek neskoršia fáza reálne otvorí.

Komponent načítava dokument pre audit rovnakým spôsobom ako pre čokoľvek iné. Nastavíte názov súboru a aktivujete ho, čo analyzuje dáta krížových odkazov (cross-reference) a katalóg dokumentu bez vykreslenia jedinej stránky. Všetko nižšie číta z tohto načítaného, nevykresleného stavu.

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := 'Incoming_Invoice.pdf';
    Pdf.Active := True;          // parses structure, renders nothing
    // audit the loaded document here
  finally
    Pdf.Free;
  end;
end;

JavaScript dokumentu v strome názvov

Prvou vecou, ktorú treba spočítať, je kód. PDF môže niesť JavaScript na úrovni dokumentu: skripty, ktoré nie sú pripojené k žiadnej stránke ani polu, ale k samotnému dokumentu, uložené v strome /Names pod položkou /JavaScript. Vyhovujúci prehliadač ich spustí pri otvorení. Toto je mechanizmus za dlhým radom PDF malvéru, pretože umožňuje súboru spustiť logiku v momente, keď naň používateľ dvakrát klikne, predtým ako prečíta jediné slovo.

Audítor chce o každom takomto skripte vedieť dva fakty: že existuje a čo obsahuje. Komponent sprístupňuje počet a umožňuje vám prečítať každú akciu ako záznam obsahujúci názov skriptu a jeho celé telo. Čítanie tela je dôležité. Skript pomenovaný Doc.0 vám nepovie nič, ale jeho text môže volať app.launchURL alebo zostaviť reťazec a odovzdať ho niekam, kam by nemal ísť. Vytiahnutie zdroja, aby si ho recenzent mohol prečítať, je hlavným zmyslom označenia súboru, ktorý spúšťa kód pri otvorení.

var
  I: Integer;
  Action: TPdfJavaScriptAction;
begin
  if Pdf.JavaScriptActionCount > 0 then
    WriteLn('WARNING: document runs ', Pdf.JavaScriptActionCount,
            ' script(s) on open');
  for I := 0 to Pdf.JavaScriptActionCount - 1 do
  begin
    Action := Pdf.JavaScriptAction[I];
    WriteLn('  script "', Action.Name, '":');
    WriteLn(Action.Script);   // full body, for a human to read
  end;
end;

Súbor s nulovým počtom dokumentových skriptov nie je automaticky bezpečný, pretože existujú aj skripty stránok a polí, ale súbor s dokumentovými skriptami si vždy zaslúži druhý pohľad. Samotný počet prítomných skriptov je užitočnou bránou a telo je to, čo mení bránu na rozhodnutie.

Akcie spustenia (Launch) a akcie URI

Ďalšie správanie, ktoré treba inventarizovať, žije na odkazoch a anotáciách. Pre audítora sú najdôležitejšie dva typy akcií. Akcia Launch spustí externý program alebo otvorí lokálny súbor pri aktivácii odkazu. Akcia URI otvorí webový cieľ. Recenzent prezerajúci podozrivý dokument by mal byť schopný vidieť, bez kliknutia na čokoľvek, že tlačidlo na tretej stránke je prepojené so spustením cmd.exe alebo s otvorením URL, ktorá nezodpovedá značke na stránke.

Komponent klasifikuje odkazy, ktoré nájde, a odkrýva typ akcie a cieľovú cestu pre každú z nich, takže audit môže uviesť každú akciu Launch a URI s jej cieľom. Ide o reportovanie, nie o vykonávanie. Audítor prečíta akciu zo štruktúry a zapíše ju. Nikdy ju nenasleduje.

Ovládací prvok prehliadača, ktorý vykresľuje dokumenty, je miestom, kde by došlo k nasledovaniu akcie, a jeho predvolený postoj je zámerne opatrný. Ovládací prvok TPdfView má sadu LinkOptions, ktorá rozhoduje o tom, ktoré typy odkazov sa spustia automaticky pri kliknutí. Jeho predvolená hodnota je [loAutoGoto, loAutoOpenURI], čo znamená, že skoky v rámci dokumentu a webové URL sa môžu otvoriť, ale loAutoLaunch chýba, takže akcie spustenia sa nikdy nespúšťajú automaticky. Pre workflow auditu idete ďalej a celú sadu vymažete, takže sa nespustí vôbec nič automaticky, kým sa stále rozhodujete, či súboru dôverovať.

// Audit posture for the viewer: nothing auto-runs, nothing auto-opens.
View.LinkOptions := [];

// The shipped default already withholds launch:
//   default = [loAutoGoto, loAutoOpenURI]
//   loAutoLaunch is NOT in the default set, so external programs
//   are never started on a stray click out of the box.

Dôvod pre predvolené zablokovanie spustenia je jednoduchý. Skok v rámci dokumentu je neškodný a URL je viditeľná a zrušiteľná, ale spustenie ľubovoľného externého programu kliknutím je tou najnebezpečnejšou vecou, ktorú môže odkaz PDF požadovať, takže je vypnuté, pokiaľ sa sami nerozhodnete inak. Audítor sa odhlasuje aj z bezpečného správania, pretože jeho úlohou je pozerať sa, nie konať.

Úroveň povolenia MDP digitálneho podpisu

Podpisy menia otázku. Obyčajný podpis svedčí o bajtoch v čase podpisu. Certifikačný podpis, druh vytvorený s pravidlom zisťovania a zabránenia zmenám dokumentu (MDP), ide ďalej: deklaruje, čo sa môže legitímne zmeniť po certifikácii dokumentu, a vyhovujúci prehliadač varuje, ak sa niekto dotkol čohokoľvek mimo tohto povolenia. Prečítanie tejto úrovne povolenia povie audítorovi, či je súbor certifikovaný a ak áno, ako prísne má byť uzamknutý.

Povolenie MDP je celé číslo s tromi definovanými hodnotami. Úroveň 1 znamená, že nie sú povolené vôbec žiadne zmeny; akákoľvek úprava porušuje certifikáciu. Úroveň 2 povoľuje vypĺňanie formulárov a podpisovanie, čo je bežný prípad pre zmluvu, ktorá sa má dokončiť a podpísať, ale inak nemeniť. Úroveň 3 navyše povoľuje anotácie k vypĺňaniu formulárov a podpisovaniu. Znalosť úrovne umožňuje vašej prijímacej logike uvažovať o zámere: dokument certifikovaný na úrovni 1, ktorý napriek tomu nesie polia formulárov alebo skripty, si odporuje, a toto protirečenie stojí za označenie.

Komponent číta počet podpisov a sprístupňuje každý ako záznam, ktorého pole Permission nesie túto hodnotu MDP, naplnenú priamo z podkladového volania FPDFSignatureObj_GetDocMDPPermission. Povolenie s hodnotou nula znamená, že podpis nie je certifikačným podpisom (DocMDP), takže neexistuje žiadne uzamknutie na úrovni dokumentu, ktoré by bolo potrebné nahlásiť.

var
  I: Integer;
  Sig: TPdfSignature;
begin
  if Pdf.SignatureCount = 0 then
    WriteLn('document is not signed')
  else
    for I := 0 to Pdf.SignatureCount - 1 do
    begin
      Sig := Pdf.Signature[I];
      case Sig.Permission of
        1: WriteLn('certified: no changes allowed');
        2: WriteLn('certified: form fill and signing allowed');
        3: WriteLn('certified: form fill, signing and annotations allowed');
      else
        WriteLn('signed, but not a DocMDP certification');
      end;
    end;
end;

Audit tu neoveruje kryptografiu podpisu; overenie certifikačného reťazca je samostatná zaležitosť. To, čo správa uvádza, je deklarovaný zámer: tento súbor tvrdí, že bol uzamknutý na tejto úrovni. To je presne kontext, ktorý recenzent potrebuje na posúdenie, či neskoršie zmeny alebo samotná prítomnosť aktívneho obsahu sú v súlade s tým, ako autor dokument spečatil.

Zvyšok povrchu: vnorené súbory a XFA

Kompletný inventár dopĺňajú ďalšie dve položky. Vnorené súbory sú celé dokumenty prenášané v PDF ako prílohy a sú klasickým spôsobom doručenia, pretože správa vyzerajúca nevinne môže vo svojom strome príloh niesť spustiteľný súbor alebo druhé škodlivé PDF. Komponent sprístupňuje počet príloh a názov každej z nich, takže audit môže uviesť, čo sa v dokumente vezie bez toho, aby čokoľvek extrahoval alebo otváral.

Ďalším príznakom je prítomnosť XFA. Formulár XFA nahrádza statický AcroForm XML architektúrou formulárov, ktorá prináša vlastný model vykresľovania a skriptovania, čo je väčší a zložitejší povrch než bežný formulár. Na zaznamenanie jeho prítomnosti nemusíte XFA spracovávať; samotná jeho prítomnosť je signálom, že súbor nesie bohatšiu interaktívnu vrstvu, ktorá stojí za bližší pohľad. Komponent to reportuje ako jednu boolovskú hodnotu.

var
  I: Integer;
begin
  if Pdf.XFA then
    WriteLn('NOTE: document contains an XFA form layer');

  if Pdf.AttachmentCount > 0 then
  begin
    WriteLn('embedded files: ', Pdf.AttachmentCount);
    for I := 0 to Pdf.AttachmentCount - 1 do
      WriteLn('  - ', Pdf.AttachmentName[I]);
  end;
end;

Jedna rutina len na čítanie, ktorá zapisuje správu

Spojte jednotlivé časti a audit je jedinou procedúrou, ktorá načíta dokument, spočíta jeho skripty a ich telá, uvedie ciele akcií Launch a URI, nahlási úroveň povolenia MDP podpisu, zaznamená prílohy a XFA a zapíše zistenia do logu. Nevykresľuje nič, takže je lacný a nedá sa oklamať zobrazením nepriateľského obsahu stránky. Výstupom je plochý, človekom čitateľný záznam, na základe ktorého môže konať recenzent alebo nadväzujúce pravidlo.

Tvar, ktorý v praxi dobre funguje, spočíva v zhromažďovaní každého zistenia do riadku, pričom pred tie skutočne rizikové sa pridá predpona, aby sa zaradili na začiatok frontu recenzií, a celá táto správa sa uloží vedľa súboru. A dokument bez skriptov, bez akcií spustenia, bez príloh, bez XFA a buď bez podpisu, alebo s konzistentnou certifikáciou prejde potichu. A dokument, ktorý spustí niekoľko príznakov naraz, je tým, ktorý by mal človek vidieť predtým, ako ho akákoľvek neskoršia fáza otvorí. Audit neurobí rozhodnutie o dôvere za vás. Zabezpečuje však, aby toto rozhodnutie bolo informované a nie slepé.

Akonáhle súbor prejde auditom a vy sa naň potrebujete pozrieť, urobte to s obmedzeniami a nie v predvolenom prehliadači. Prístup v našom sprievodcovi vytvorením zabezpečeného náhľadu PDF v Delphi ukazuje, ako zabrániť automatickému spracovaniu odkazov a aktívnemu obsahu počas riadeného prezerania. Pre začlenenie tohto výpočtu do kompletného prijímacieho reťazca s nástrojmi pre recenzentov si pozrite článok o prijímacom a recenznom pracovisku PDF. Obe stavajú na rovnakom základe len na čítanie a bez vykresľovania a dodávajú sa ako súčasť PDFium Component pre Delphi a C++Builder spolu s rozhraniami API pre vykresľovanie, text, formuláre a podpisy, o ktorých sa píše inde na tomto blogu.