Technical Article

Varen predogled PDF-jev v aplikacijah Delphi s komponento PDFium Component

Predogled nezaupljivega PDF-ja znotraj vaše aplikacije je izvajalska odločitev, pri čemer ni pomemben vizualni okvir pregledovalnika, temveč to, česa področje za predogled samo po sebi noče storiti. Datoteke ne zapisujte na disk. Ne dovolite, da njene povezave sprožijo zunanje ukaze. Prilogam ne dodeljujte sistemske poti. Večina škode zaradi zlonamernega dokumenta ne izvira iz same napake v mehanizmu, temveč iz tega, da pregledovalnik izvaja povsem običajne stvari z vhodnimi podatki napadalca: odpre povezavo file:// do deljenega mesta UNC, kar povzroči uhajanje poverilnic NTLM, pusti začasno kopijo v začasnem imeniku ali kopira vgrajene zlonamerne kode tja, kamor kaže niz imena datoteke. PDFium Component je pregledovalnik PDF z izvorno kodo za Delphi, C++Builder in Lazarus, ki vam ponuja ustrezna stikala na dosegu roke: zastavico ob nalaganju, ki onemogoči skripte, dogodke ob kliku na povezavo, ki jih lahko zavrnete, dostop do prilog prek lastne kode in branje bitov z dovoljenji. Spodnji vrstni red spremlja dokument od trenutka, ko prispe, do trenutka, ko uporabnik klikne nekaj v njem.

Model groženj za področje predogleda

Bodimo iskreni glede tega, kaj vam prinaša "varen predogled". Upodabljalnik razčleni nezaupljive bajte ne glede na to, kaj storite, varnostna zaščita samega mehanizma pa je osnova, na kateri gradite. Vse nad tem je politika aplikacije: ali se skripte inicializirajo, kaj se zgodi ob kliku na povezavo, ali lahko vgrajene datoteke dosežejo disk ter ali so odložišče in tiskalnik vrata ali stene. Nekaj, kar lahko takoj črtate, je stikalo mehanizma FPDF_SetSandBoxPolicy. Večina omejitev mehanizma je vgrajena ob prevajanju, to stikalo v praksi spremeni le malo, zanašanje nanj pri izolaciji pa ustvari le lažen občutek varnosti. Ko je vhod resnično zlonameren (na primer javni portal za prenos datotek), je edina prava izolacija izrisovanje v ločenem procesu z nizkimi pravicami in pošiljanje bitnih slik v uporabniški vmesnik. Zastavice znotraj procesa so politika delovanja. Niso pa izolacija.

Dve površini je enostavno pozabiti prav zato, ker se ju noben klik ne dotakne. Prva so začasne datoteke. Če vaš cevovod uvožene dokumente pred predogledom shrani na disk, te začasne kopije preživijo sejo, razen če jih nekaj preverljivo izbriše. Datoteka, ki jo je mogoče "obnoviti iz začasnega imenika", je tiho zaobšla vse nadzorne mehanizme, ki jih izvaja samo področje za predogled. Namesto tega naložite datoteko iz pomnilnika prek TPdfStreamAdapter, da zlonamerni bajti nikoli ne dobijo lastne sistemske poti. Druga površina je odložišče (clipboard). Predogled, ki omogoča izbiranje in kopiranje, je dokument že izvozil (po en zaslon naenkrat) in nobeno prestrezanje povezav tega ne bo preprečilo.

Onemogočanje JavaScripta ob nalaganju in ne v uporabniškem vmesniku

JavaScript v dokumentu se v komponenti PDFium Component inicializira le skupaj z okoljem za izpolnjevanje obrazcev. Nalaganje z nastavitvijo FormFill := False zato onemogoči skriptni jezik pri koreninah, namesto da bi le zatiralo njegove simptome:

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;

Ta kompromis je resničen in sodi v vaše specifikacije. Z onemogočenim izpolnjevanjem obrazcev so onemogočeni tudi legitimna interakcija z AcroForm obrazci in validacijski skripti; polja se izrišejo s svojim zadnjim shranjenim videzom, vendar jih ni mogoče urejati. Za področje predogleda je to običajno pravilna odločitev, saj predogled pomeni gledanje, ne pa izpolnjevanje. Če pa isto okno služi tudi kot površina za izpolnjevanje obrazcev za zaupanja vredne notranje dokumente, je rešitev v dveh poteh nalaganja z eksplicitno odločitvijo o zaupanju, ne pa v eni poti s kompromisno nastavitvijo, ki je preveč ohlapna za zlonamerni primer in pretesna za zaupanja vrednega. Stran za izpolnjevanje obrazcev ima svoje lastne pasti, ki jih obravnava članek o navigaciji po obrazcih in regeneraciji videza.

Povezave: privzeti upravitelj sproži zunanje ukaze

Če jih pustite nespremenjene, kliki na povezave vodijo neposredno v operacijski sistem. Privzete nastavitve LinkOptions pregledovalnika vključujejo možnost loAutoOpenURI, kar predstavlja tveganje za uhajanje podatkov preko povezav file:// do UNC deljenih mest. Dva dogodka tvorita glavno kontrolno točko: OnWebLinkClick za URL-je, zaznane v besedilu strani, in OnAnnotationLinkClick za anotacije povezav, ki prenašajo URI ali zagonske akcije. V obeh primerih brezpogojno nastavite Handled := True pred kakršno koli odločitvijo, nato pa dovolite le tisto, kar dovoljuje varnostna politika. Kot drugo raven zaščite odstranite loAutoOpenURI iz LinkOptions za zlonamerne vhode in zagotovite, da se možnost loAutoLaunch (ki je privzeto izklopljena) nikoli ne vrne preko kopirane konfiguracije:

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;

Dve podrobnosti določata, ali to dejansko drži. Prvič, preverjanje sheme mora biti preverjanje predpone na surovem nizu pred kakršnim koli razčlenjevanjem, sia so povezave file://, poti UNC in eksotične sheme ravno tiste vrednosti, ki povzročijo sesutje preprostega razčlenjevalnika URL-jev ali pa se izmuznejo tistemu, ki preveč agresivno normalizira podatke. Drugič, vsako blokado zabeležite skupaj z identiteto dokumenta. Nekaj blokiranih povezav file:// je le šum v ozadju; njihov izbruh v številnih prejetih dokumentih v kratkem času pa je incident, o katerem bi vaša varnostna ekipa raje slišala od vas kot pa iz drugih virov.

Priloge: politika končnic in ime datoteke, ki ga niste izbrali sami

PDF je vsebnik, lastnost AttachmentCount skupaj z AttachmentName[] pregledovalnika pa vam pove, kaj prenaša, preden se karkoli dotakne diska. Tukaj sta pomembna dva ločena nadzorna mehanizma, pri čemer je le eden očiten. Očiten je politika tipov: seznam dovoljenih (allowlist) končnic, ki jih je sploh mogoče izvoziti. Manj opazen pa je dejstvo, da je ime priloge podatek, ki ga v celoti nadzoruje napadalec. Vgrajeno ime, kot je ..\..\Startup\update.exe, spremeni neprevidno shranjevanje v prehod imenika (path traversal), ki odloži izvedljivo datoteko v mapo, ki jo sistem Windows zažene ob prijavi. Komponenta vam preda vsebino kot bajte prek Attachment[] in omogoča vaši kodi izbiro poti, zato to pot zgradite iz očiščenega osnovnega imena (basename) in nikoli iz surovega vgrajenega niza:

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;

Prednost dajte seznamu dovoljenih (allowlist). Seznam prepovedanih (blocklist) "nevarnih" končnic je tekma, ki jo boste izgubili tisti dan, ko nekdo uporabi končnico, za katero še niste slišali. Seznam dovoljenih končnic, kot so .pdf, .png in .csv, ob napaki onemogoči dostop.

Kaj dejansko obljubljajo dovoljenja za šifriranje

Standardni varnostni upravitelj standarda ISO 32000-1 kodira zastavice dovoljenj za tiskanje, kopiranje vsebine in spreminjanje, lastnosti Permissions in UserPermissions pa jih prikažejo kot surove bitne maske, ko se dokument odpre. Tabela 22 standarda ISO 32000-1 opredeljuje te bite, nešifrirana datoteka pa poroča o vseh nastavljenih bitih. Preberite jih in jih upoštevajte v svoji ukazni plasti, vendar bodite jasni glede njihove narave. Pri dokumentu, šifriranem z lastniškim geslom in praznim uporabniškim geslom, se vsebina ob odpiranju v celoti dešifrira, zastavice pa so le zahteva pregledovalnikom, naj te omejitve upoštevajo, in ne prisilni mehanizem. To ima dve posledici, ki vlečeta v nasprotne smeri. Zastavic z dovoljenji uporabnikom nikoli ne predstavljajte kot varnostne lastnosti prejetih dokumentov, saj to niso. Hkrati pa upoštevajte bit za dostopnost (bit 10), tudi če je splošno kopiranje (bit 5) onemogočeno; dostop za bralnike zaslona je v modelu dovoljenj namenoma ločen, onemogočanje tega dostopa zaradi izklopljenega kopiranja pa škoduje podporni tehnologiji brez kakršne koli varnostne koristi.

Onemogočena dejanja uveljavljajte na ravni ukazov in ne s skrivanjem gumbov v orodni vrstici. Ctrl+C, kontekstni meniji in vlečenje z miško zaobilazijo orodno vrstico; eno samo preverjanje dovoljenja znotraj ukaza za kopiranje pa ne zaobide ničesar.

Za dokumente, ki zahtevajo uporabniško geslo, dodelite Password pred nastavitvijo Active := True in obravnavajte to vrednost kot skrivnost: pridobite jo iz shrambe poverilnic za vsako sejo posebej, preprečite njen izpis v dnevnikih in poročilih o sesutju ter je nikoli ne shranjujte poleg dokumenta. Področje za predogled, ki predpomni gesla "zaradi priročnosti", je tiho postalo zbirka gesel brez kakršne koli varnostne zaščite.

Tiskanje si zasluži lastno odločitev in ne zgolj prevzema pravila, ki velja za kopiranje. Fizični tisk je po definiciji nereviziran, vendar popolna blokada tiskanja uporabnike običajno prisili v delanje posnetkov zaslona, kar je slabše na vseh ravneh. Pogosta vmesna pot je, da tiskanje dovolite, vendar vsako stran opremite z identiteto uporabnika in časovnim žigom, kar se uveljavi znotraj ukaza za tiskanje. Pri tem imejte prava pričakovanja: vodni žig je namenjem odvračanju in ugotavljanju avtorstva. Ni pa preprečevanje.

Kaj bi vam morala triaža povedati že prej

Področje za predogled sprejema boljše odločitve, če dokument prispe z že pripravljeno mapo s podatki: ali je šifriran ali ne, ali vsebuje JavaScript, seznam prilog in vrsto obrazca. Ta pregled sodi pred sam pregledovalnik, vzorec v članku o izdelavi delovnega okolja za pregled prejetih PDF-jev pa ustvari natanko tiste zastavice, ki jih varnostna politika predogleda želi uporabiti. Datoteke, ki jih je triaža označila kot tvegane, se samodejno odprejo prek zaščitene poti; običajni dokumenti ohranijo svoje priročnosti. Povežite obe stopnji z enim skupnim objektom politike namesto z dvema konfiguracijskima zaslonoma, ki se bosta do druge izdaje programske opreme zagotovo oddaljila, ne glede na to, kako skrbno ju napišete prvič.

Kje poteka meja med izvajanjem znotraj procesa (in-process) in zunaj njega (out-of-process), je odvisno od tega, kdo vam pošilja datoteke. Za običajen poslovni sprejem so pošiljatelji dokumentov znani in le neprevidni, zato je predogled znotraj procesa z onemogočenim skriptiranjem in prestreženimi povezavami sprejemljiva zaščita. Za anonimne javne prenose to ne velja in nobena količina nastavitev znotraj procesa ne bo zadoščala; te izrišite v ločenem delovnem procesu z nizkimi pravicami in pošiljajte bitne slike v uporabniški vmesnik, tako da vas napaka v mehanizmu stane le enega delovnega procesa in ne celotne gostiteljske aplikacije. To razdelitev določite premišljeno in zapišite, v katero skupino sodi posamezna pot sprejema dokumentov, saj so stroški napačne ocene nesimetrični.

Licenciranje, varnostni del API-ja in predstavitev zaščitenega pregledovalnika so na voljo na strani izdelka: PDFium Component.