Technical Article

Zabezpečená ukážka PDF v aplikáciách Delphi pomocou komponentu PDFium

Zobrazenie ukážky nedôveryhodného súboru PDF vo vašej vlastnej aplikácii je rozhodnutím o spustení, pričom dôležitý nie je vzhľad samotného prehliadača, ale to, čo panel odmietne vykonať sám. Neukladajte súbor na disk. Nedovoľte jeho odkazom spúšťať externé príkazy. Nedávajte jeho prílohám cestu. Väčšina škôd spôsobených nepriateľským dokumentom nevyplýva z exploitu vykresľovacieho jadra, ale z toho, že prehliadač robí úplne bežné veci so vstupom dodaným útočníkom: otvára odkaz typu file:// na zdieľaný prostriedok UNC, ktorý prepúšťa prihlasovacie údaje NTLM, ponecháva dočasnú kópiu v dočasnom adresári alebo kopíruje vložené objekty kamkoľvek, kam ho navedie názov súboru. PDFium Component je PDF prehliadač so zdrojovým kódom pre Delphi, C++Builder a Lazarus, ktorý umiestňuje príslušné prepínače priamo do vášho dosahu: príznak pri načítaní, ktorý zakáže skriptovanie, udalosti kliknutia na odkaz, ktoré môžete vetovať, prístup k prílohám spustený prostredníctvom vášho vlastného kódu a bity oprávnení, ktoré môžete čítať. Nižšie uvedený postup sleduje dokument od momentu jeho prijatia až po okamih, kedy používateľ na niečo klikne.

The threat model of a preview pane

Buďte úprimní v tom, čo vám „bezpečné zobrazenie ukážky“ reálne prináša. Vykresľovacie jadro analyzuje nedôveryhodné bajty bez ohľadu na to, čo urobíte, a vlastné zabezpečenie jadra je základom, na ktorom stojíte. Všetko nad týmto základom sú pravidlá aplikácie: či sa inicializujú skripty, čo urobí kliknutie na odkaz, či môžu vložené súbory dosiahnuť disk a či sú schránka a tlačiareň dverami alebo stenami. Jedna vec, ktorú môžete hneď na začiatku vylúčiť, je prepínač jadra FPDF_SetSandBoxPolicy. Väčšina obmedzení jadra je skompilovaná priamo v ňom, prepínač v praxi mení len málo a spoliehať sa naň pri izolácii len vytvára falošný pocit zabezpečenia. Ak je vstup skutočne nepriateľský, napríklad verejný portál na nahrávanie súborov, jedinou skutočnou izoláciou je vykresľovanie v samostatnom procese s nízkymi oprávneniami a prenos bitmapových obrázkov do používateľského rozhrania. Príznaky v rámci procesu sú iba pravidlami, nie skutočným obmedzením.

Na dve miesta sa ľahko zabúda práve preto, že sa ich nedotkne žiadne kliknutie. Prvým sú dočasné súbory. Ak váš systém ukladá prichádzajúce dokumenty na disk pred zobrazením ukážky, tieto uložené kópie prežijú reláciu, pokiaľ ich niečo overiteľne neodstráni, a súbor, ktorý je „obnoviteľný z dočasného adresára“, potichu obíde akúkoľvek kontrolu, ktorú samotný panel vynucuje. Namiesto toho načítajte súbory z pamäte pomocou TPdfStreamAdapter, aby nepriateľské bajty nikdy nezískali vlastnú cestu na disku. Druhým miestom je schránka. Ukážka, ktorá umožňuje výber a kopírovanie, už dokument exportovala, po jednotlivých obrazovkách, a žiadne zachytávanie odkazov tomu nezabráni.

Vypnite JavaScript pri načítaní, nie v používateľskom rozhraní

JavaScript v dokumente sa v komponente PDFium Component inicializuje iba spolu s prostredím na vypĺňanie formulárov. Načítanie s nastavením FormFill := False preto zakáže skriptovanie priamo pri zdroji namiesto potláčania jeho príznakov:

procedure TPreviewPane.LoadUntrusted(const FilePath: string);
begin
  Pdf.FileName := FilePath;
  Pdf.FormFill := False;     // no form environment, hence no JavaScript engine
  Pdf.Active := True;

  FPermissions := Pdf.Permissions;   // raw flag word; all bits set = unrestricted
end;

Tento kompromis je reálny a patrí do vašich špecifikácií. Pri zakázanom vypĺňaní formulárov zaniká aj legitímna interakcia s AcroForm a validačné skripty. Polia sa vykreslia so svojím naposledy uloženým vzhľadom, ale nemožno ich upravovať. Pre panel ukážky je to zvyčajne správne rozhodnutie, pretože ukážka znamená prezerať, nie vypĺňať. Ak však to isté okno slúži aj na vypĺňanie formulárov pre dôveryhodné interné dokumenty, riešením sú dve cesty načítania s jasným rozhodnutím o dôveryhodnosti medzi nimi, nie jedna cesta s kompromisným nastavením, ktoré je príliš voľné pre nepriateľské prípady a príliš obmedzujúce pre tie dôveryhodné. Vypĺňanie formulárov má svoje vlastné úskalia, ktoré sú popísané v článku o navigácii vo formulárových poliach a regenerácii vzhľadu.

Odkazy: predvolený obslužný program spúšťa externé príkazy

Ak ponecháte predvolené nastavenia, kliknutia na odkazy smerujú priamo do operačného systému. Predvolené možnosti LinkOptions prehliadača zahŕňajú loAutoOpenURI, čo predstavuje priame riziko úniku cez prepojenie file:// na zdieľaný prostriedok UNC. Dve udalosti tvoria kľúčové body kontroly: OnWebLinkClick pre adresy URL detegované v texte stránky a OnAnnotationLinkClick pre anotácie odkazov obsahujúce URI alebo spúšťacie akcie. V oboch prípadoch bezpodmienečne nastavte Handled := True pred akýmkoľvek rozhodnutím a potom povoľte iba to, čo umožňujú bezpečnostné pravidlá. Ako druhú vrstvu ochrany odstráňte loAutoOpenURI z LinkOptions pre rizikové vstupy a uistite sa, že sa možnosť loAutoLaunch, ktorá je predvolene vypnutá, znova neobjaví prostrednívom skopírovanej konfigurácie:

procedure TPreviewPane.PdfViewWebLinkClick(Sender: TObject;
  const Url: WString; var Handled: Boolean);
begin
  Handled := True;   // never fall through to the default shell behavior

  if (AnsiStartsText('https://', Url) or AnsiStartsText('http://', Url))
    and HostIsAllowed(Url) then
    OpenInBrowser(Url)
  else
    FAudit.LogBlockedLink(FDocumentId, Url);
end;

O účinnosti tohto riešenia rozhodujú dva detaily. Po prvé, kontrola schémy musí byť kontrolou prefixu na nespracovanom reťazci ešte pred akoukoľvek analýzou, pretože práve protokoly file://, cesty UNC a exotické schémy sú hodnoty, ktoré spôsobia zlyhanie jednoduchého URL parsera alebo prejdú cez ten, ktorý normalizuje príliš agresívne. Po druhé, zaznamenávajte každé zablokovanie spolu s identifikáciou dokumentu. Pár zablokovaných odkazov file:// je len bežný šum. Ich náhly nárast v mnohých prichádzajúcich dokumentoch v krátkom čase je však incident, o ktorom by sa váš bezpečnostný tím mal dozvedieť radšej od vás než z iných zdrojov.

Prílohy: pravidlá pre prípony a názov súboru, ktorý ste si nevybrali

Súbor PDF je kontajner a vlastnosti AttachmentCount spolu s AttachmentName[] vám prezradia, čo obsahuje ešte pred tým, ako sa čokoľvek zapíše na disk. Dôležité sú tu dva samostatné ovládacie prvky, z ktorých iba jeden je zrejmý. Tým zrejmým sú pravidlá pre typy súborov: zoznam povolených prípon (allowlist), ktoré je vôbec možné exportovať. Tým menej zrejmým je fakt, že názov prílohy predstavuje dáta plne pod kontrolou útočníka. Vložený názov typu ..\..\Startup\update.exe premení neopatrné uloženie na útok typu path traversal, ktorý uloží spustiteľný súbor do priečinka, ktorý systém Windows spúšťa pri prihlásení. Komponent vám odovzdá dáta prílohy vo forme bajtov prostredníctvom Attachment[] a umožní vášmu kódu vybrať cestu, preto túto cestu vytvorte z očisteného názvu súboru a nikdy nie z pôvodného vloženého reťazca:

procedure TPreviewPane.ExportAttachment(Index: Integer; const TargetDir: string);
var
  RawName, SafeName, Ext: string;
  Data: TBytes;
begin
  RawName := string(Pdf.AttachmentName[Index]);
  SafeName := ExtractFileName(RawName);    // strips any path components
  Ext := LowerCase(ExtractFileExt(SafeName));

  if not FAllowedExt.Contains(Ext) then    // allowlist, not blocklist
    raise EPreviewPolicy.CreateFmt('Attachment type %s blocked by policy', [Ext]);

  Data := Pdf.Attachment[Index];           // embedded payload as raw bytes
  TFile.WriteAllBytes(
    IncludeTrailingPathDelimiter(TargetDir) + SafeName, Data);
end;

Uprednostňujte prístup založený na zozname povolených prípon. Zoznam zakázaných prípon (blocklist) je boj, ktorý prehráte v deň, keď niekto zneužije príponu, o ktorej ste nikdy nepočuli. Zoznam povolených prípon obsahujúci napríklad .pdf, .png a .csv pri probléme prístup bezpečne zablokuje.

Čo v skutočnosti zaručujú oprávnenia šifrovania

Štandardný bezpečnostný handler podľa normy ISO 32000-1 kóduje príznaky oprávnení pre tlač, kopírovanie obsahu a úpravy, pričom vlastnosti Permissions a UserPermissions ich po otvorení dokumentu zobrazujú ako nespracované bitové masky. Tabuľka 22 v norme ISO 32000-1 definuje tieto bity, a nezašifrovaný súbor hlási všetky bity ako nastavené. Čítajte ich a rešpektujte ich vo vašej príkazovej vrstve, ale ujasnite si, čo vlastne znamenajú. Pri dokumente zašifrovanom heslom vlastníka a s prázdnym heslom používateľa sa obsah pri otvorení plne dešifruje a príznaky sú len požiadavkou na kompatibilné prehliadače, nie vynucovacím mechanizmom. To má dva dôsledky, ktoré idú opačnými smermi. Nikdy neprezentujte príznaky oprávnení používateľom ako bezpečnostnú vlastnosť prijatých dokumentov, pretože ňou nie sú. Zároveň rešpektujte bit pre extrakciu pre prístupnosť (bit 10) aj tam, kde je zakázané všeobecné kopírovanie (bit 5). Prístup pre čítačky obrazovky je v modeli oprávnení zámerne vyčlenený samostatne a jeho zrušenie z dôvodu zakázaného kopírovania poškodzuje asistenčné technológie bez akéhokoľvek prínosu pre bezpečnosť.

Vynucujte zakázané akcie na úrovni príkazov, nie skrývaním tlačidiel na paneli nástrojov. Skratka Ctrl+C, kontextové ponuky a výber ťahaním myšou obchádzajú panel nástrojov. Jediná kontrola oprávnení vo vnútri príkazu na kopírovanie však neobíde nič.

Pri dokumentoch, ktoré vyžadujú používateľské heslo, priraďte vlastnosť Password ešte pred nastavením Active := True a zaobchádzajte s touto hodnotou ako s tajomstvom: získajte ju z úložiska poverení pre každú reláciu zvlášť, neuvádzajte ju v logoch ani správach o zlyhaní a nikdy ju neukladajte vedľa dokumentu. Panel ukážky, ktorý ukladá heslá do vyrovnávacej pamäte „pre pohodlie“, sa potichu stal databázou hesiel bez akejkoľvek ochrany.

Tlač si vyžaduje vlastné rozhodnutie a nemala by len dediť pravidlá nastavené pre kopírovanie. Fyzický výtlačok je zo svojej podstaty neauditovateľný, no úplné zablokovanie tlače zvyčajne vedie používateľov k vytváraniu snímok obrazovky, čo je horšie v každom ohľade. Bežným stredným riešením je povoliť tlač, ale na každú stránku umiestniť identitu používateľa a časovú pečiatku, čo sa vynúti priamo v príkaze tlače. Majte však správne očakávania: vodoznak je prostriedkom odstrašenia a pripísania zodpovednosti, nie prevenciou.

Čo vám mal povedať už vstupný proces

Panel ukážky robí lepšie rozhodnutia, ak prichádzajúci súbor už obsahuje sprievodné informácie: či je zašifrovaný, či obsahuje JavaScript, zoznam príloh alebo typ formulára. Táto kontrola patrí pred samotný prehliadač a postup opísaný v článku o vytvorení nástroja na kontrolu prichádzajúcich PDF generuje presne tie príznaky, ktoré bezpečnostné pravidlá ukážky vyžadujú. Súbory, ktoré vstupný proces označí za rizikové, sa automaticky otvoria cez zabezpečenú cestu, zatiaľ čo bežné dokumenty si zachovajú svoje pohodlie. Prepojte tieto dve fázy s jedným zdieľaným objektom pravidiel namiesto dvoch samostatných konfiguračných obrazoviek, ktoré by sa od druhej verzie začali líšiť bez ohľadu na to, ako dôkladne ich na začiatku navrhnete.

Hranica medzi spracovaním v rámci procesu (in-process) a mimo neho (out-of-process) závisí od toho, kto vám súbory posiela. Pri bežnom firemnom vstupe sú odosielatelia známi a nanajvýš neopatrní. Vtedy je ukážka v rámci procesu so zakázaným skriptovaním a zachytávaním odkazov obhájiteľnou úrovňou ochrany. Pri anonymných verejných nahrávaniach to však neplatí a žiadne nastavenie príznakov v rámci procesu to nezmení. Vykresľujte tieto súbory v samostatnom procese s nízkymi oprávneniami a posielajte do rozhrania iba bitmapy, aby vás chyba v jadre stála iba pád pomocného procesu, a nie celej hostiteľskej aplikácie. Rozhodnite sa o tomto rozdelení zámerne a zdokumentujte, do ktorej kategórie patrí každá cesta prijímania súborov, pretože následky nesprávneho odhadu sú asymetrické.

Licencovanie, API súvisiace s bezpečnosťou a ukážka zabezpečeného prehliadača sú k dispozícii na produktovej stránke: PDFium Component.