PDF není jen papír. Je to kontejner, který může nést skripty spouštěné při otevření souboru, odkazy spouštějící externí programy, odkazy směřující na webové servery, soubory vnořené do jiných souborů a podpis, který potvrzuje, že se dokument nezměnil od chvíle, kdy se za něj někdo zaručil. Když soubor dorazí ze zdroje, který nemáte pod kontrolou, nejbezpečnějším prvním krokem není jeho vykreslení. Je jím přečtení toho, co o sobě soubor říká, a vytvoření inventáře všeho, o co by se mohl pokusit, aby se člověk mohl rozhodnout, zda vůbec patří do vašeho pracovního postupu.
Tento článek provází statickým auditem tohoto rizikového povrchu v režimu pouze pro čtení s využitím komponenty PDFium pro Delphi a Lazarus. Audit nikdy nevykresluje stránku. Analyzuje strukturu dokumentu, jmenuje části souboru nesoucí chování a zapisuje jednoduchou zprávu. Je to rozdíl mezi tím, když cizince u dveří požádáte, aby vyprázdnil kapsy, a tím, když mu důvěřujete jen proto, že se usmál.
Co audit je a co není
Mějte jasno o hranicích. Náhled v sandboxu vykresluje soubor za přísných omezení, takže si jej uživatel může prohlédnout, aniž by se soubor dotkl zbytku systému. Audit přichází ještě předtím. Jedná se o kontrolu bez vykreslování, jejímž jediným výstupem je popis povrchu hrozeb: které skripty existují, které akce jsou připojeny k odkazům, zda je soubor podepsán a jak přísně, a co je k němu připojeno. Spouštíte jej, když dokument překročí hranici důvěry, při příjmu z e-mailu, nahrávacího formuláře nebo partnerského kanálu, předtím, než jej jakákoli pozdější fáze skutečně otevře.
Komponenta načítá dokument pro audit stejně jako pro jakýkoli jiný účel. Nastavíte název souboru a aktivujete jej, což analyzuje křížové odkazy (cross-reference) a katalog dokumentu bez vykreslení jediné stránky. Vše níže uvedené čte z tohoto načtené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 ve stromu názvů
První věcí, kterou je třeba vypsat, je kód. PDF může nést JavaScript na úrovni dokumentu: skripty, které nejsou připojeny k žádné stránce nebo poli, ale k samotnému dokumentu, uložené ve stromu /Names pod položkou /JavaScript. Kompatibilní prohlížeč je spouští při otevření. To je mechanismus stojící za dlouhou řadou malwaru v PDF, protože umožňuje souboru spustit logiku v okamžiku, kdy na něj uživatel dvakrát klikne, ještě než přečte jediné slovo.
Auditor chce o každém takovém skriptu vědět dva fakty: že existuje a co obsahuje. Komponenta zpřístupňuje počet a umožňuje číst každou akci jako záznam obsahující název skriptu a jeho celé tělo. Čtení těla skriptu je důležité. Skript pojmenovaný Doc.0 vám nic neřekne, ale jeho text může volat app.launchURL nebo sestavovat řetězec a předávat jej někam, kam by neměl jít. Vytáhnutí zdrojového kódu tak, aby si jej mohl recenzent přečíst, je celým smyslem označování souborů, které spouštějí kód při otevření.
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;
Soubor s nulovým počtem dokumentových skriptů není automaticky bezpečný, protože existují i skripty stránek a polí, ale soubor s dokumentovými skripty si vždy zaslouží zvýšenou pozornost. Samotný počet přítomných skriptů je užitečným filtrem a tělo skriptu je to, co umožňuje učinit rozhodnutí.
Spouštěcí akce a akce URI
Další chování, které je třeba zmapovat, se nachází na odkazech a anotacích. Pro auditora jsou nejdůležitější dva typy akcí. Akce Launch (spuštění) spouští externí program nebo otevírá lokální soubor při aktivaci odkazu. Akce URI otevírá webový cíl. Recenzent prohlížející podezřelý dokument by měl být schopen vidět, aniž by na cokoli klikal, že tlačítko na třetí stránce je nastaveno ke spuštění cmd.exe nebo k otevření adresy URL, která neodpovídá identitě na stránce.
Komponenta klasifikuje nalezené odkazy a vystavuje typ akce a cílovou cestu pro každou z nich, takže audit může vypsat každou akci Launch a URI s jejím cílem. Jedná se o reportování, nikoli o spuštění. Auditor čte akci ze struktury a zaznamenává ji. Nikdy ji nenásleduje.
Ovládací prvek prohlížeče, který vykresluje dokumenty, je místem, kde by k následování akce došlo, a jeho výchozí nastavení je záměrně opatrné. Prvek TPdfView má sadu LinkOptions, která určuje, které typy odkazů se spouštějí automaticky při kliknutí. Výchozí hodnota je [loAutoGoto, loAutoOpenURI], což znamená, že se mohou otevírat skoky v rámci dokumentu a webové adresy URL, ale loAutoLaunch chybí, takže spouštěcí akce se nikdy nespustí automaticky. Pro pracovní postup auditu jděte ještě dále a tuto sadu zcela vymažte, aby se při rozhodování o důvěryhodnosti souboru nespouštělo vůbec nic.
// 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 pro výchozí blokování spouštění je jednoduchý. Skok v rámci dokumentu je neškodný a adresa URL je viditelná a stornovatelná, ale spuštění libovolného externího programu kliknutím je tou nejnebezpečnější věcí, jakou může odkaz PDF vyžadovat, takže je zakázáno, pokud se výslovně nepřihlásíte k opaku. Auditor se odhlašuje i z bezpečného chování, protože jeho úkolem je dívat se, nikoli jednat.
Úroveň oprávnění MDP digitálního podpisu
Podpisy mění situaci. Obyčejný podpis stvrzuje stav bajtů v dobu podpisu. Certifikační podpis (vytvořený s pravidlem pro detekci a prevenci změn dokumentu – MDP) jde dále: deklaruje, co se může po certifikaci dokumentu legitimně změnit, a kompatibilní prohlížeč varuje, pokud se změní cokoli mimo toto povolení. Čtení této úrovně oprávnění sděluje auditorovi, zda je soubor certifikován, a pokud ano, jak moc má být uzamčen.
Oprávnění MDP je celé číslo se třemi definovanými hodnotami. Úroveň 1 znamená, že nejsou povoleny vůbec žádné změny; jakákoli úprava porušuje certifikaci. Úroveň 2 povoluje vyplňování formulářů a podepisování, což je běžný případ smlouvy, která má být vyplněna a podepsána, ale jinak neupravována. Úroveň 3 navíc povoluje anotace nad rámec vyplňování formulářů a podepisování. Znalost této úrovně umožňuje logice příjmu uvažovat o záměru: dokument certifikovaný na úrovni 1, který přesto obsahuje pole formuláře nebo skripty, si protiřečí, a tento rozpor stojí za to označit.
Komponenta načítá počet podpisů a vystavuje každý z nich jako záznam, jehož pole Permission nese tuto hodnotu MDP, naplněnou přímo z podkladového volání FPDFSignatureObj_GetDocMDPPermission. Oprávnění s hodnotou nula znamená, že podpis není certifikačním (DocMDP) podpisem, takže zde není žádné uzamčení na úrovni dokumentu k hlášení.
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 zde neověřuje kryptografickou platnost podpisu; ověření řetězce certifikátů je samostatnou záležitostí. To, co hlásí, je deklarovaný záměr: tento soubor říká, že byl uzamčen na této úrovni. To je přesně ten kontext, který recenzent potřebuje k posouzení, zda jsou pozdější změny nebo samotná přítomnost aktivního obsahu v souladu s tím, jak autor dokument zapečetil.
Zbytek povrchu: vnořené soubory a XFA
Kompletní inventář doplňují další dvě položky. Vnořené soubory (embedded files) jsou celé dokumenty nesené uvnitř PDF jako přílohy a představují klasický doručovací prostředek, protože nevinně vyhlížející zpráva může ve svém stromu příloh přenášet spustitelný soubor nebo druhé škodlivé PDF. Komponenta vystavuje počet příloh a název každé z nich, takže audit může vypsat, co se v dokumentu veze, aniž by cokoli extrahoval nebo otevíral.
Druhým příznakem je přítomnost XFA. Formulář XFA nahrazuje statický AcroForm XML architekturou formulářů, která přináší vlastní model vykreslování a skriptování, což je větší a složitější povrch než u běžného formuláře. Nemusíte XFA zpracovávat, abyste zaznamenali jeho přítomnost; samotný fakt, že tam je, signalizuje, že soubor nese bohatší interaktivní vrstvu stojící za bližší prozkoumání. Komponenta jej hlásí jako jeden boolean.
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 pouze pro čtení, která zapisuje zprávu
Složte jednotlivé části dohromady a audit se stane jedinou procedurou, která načte dokument, vypíše jeho skripty a jejich těla, vytvoří seznam cílů Launch a URI, ohlásí úroveň MDP podpisu, zaznamená přílohy i XFA a zapíše zjištění do protokolu. Nevykresluje nic, takže je nenáročná na výkon a nelze ji oklamat zobrazením nepřátelského obsahu stránky. Výstupem je plochý, lidsky čitelný záznam, na jehož základě může jednat recenzent nebo navazující pravidlo.
Formát, který v praxi dobře funguje, spočívá v shromažďování každého zjištění jako řádku, přičemž ty skutečně rizikové dostanou předponu, aby se zařadily na začátek fronty k recenzi, a celý výsledek se uloží vedle souboru. Dokument bez skriptů, bez spouštěcích akcí, bez příloh, bez XFA a s čistou certifikací nebo bez podpisu projde bez povšimnutí. Dokument, který aktivuje několik příznaků najednou, je ten, který by měl člověk vidět dříve, než jej otevře jakákoli další fáze zpracování. Audit za vás nerozhoduje o důvěře. Zajišťuje pouze, aby toto rozhodnutí bylo podložené informacemi, nikoli slepé.
Jakmile soubor projde auditem a vy se na něj potřebujete podívat, učiňte tak za omezených podmínek, nikoli ve výchozím prohlížeči. Přístup v našem průvodci vytvořením zabezpečeného náhledu PDF v Delphi ukazuje, jak zabránit automatickému zpracování odkazů a spuštění aktivního obsahu během kontrolovaného prohlížení. Pro začlenění tohoto výčtu do kompletní pipeline příjmu s nástroji pro recenzenty viz článek o workbench pro příjem a recenzi PDF. Obojí staví na stejném základu pouze pro čtení a bez vykreslování a dodává se jako součást produktu PDFium Component pro Delphi and C++Builder spolu s rozhraními API pro vykreslování, text, formuláře a podpisy, o nichž se píše na jiných místech tohoto blogu.