Tekninen artikkeli

Turvallinen PDF-esikatselu Delphissä PDFium Componentilla

Epäluotettavan PDF:n esikatselu oman sovelluksesi sisällä on suorituspäätös, ja osa joka merkitsee ei ole katselimen kromi vaan se mitä ruutu kieltäytyy tekemästä omillaan. Älä kirjoita tiedostoa levylle. Älä anna sen linkkien suoltaa ulos. Älä anna sen liitteille polkua. Suurin osa vahingosta vihamieliseltä dokumentilta tulee ei moottori-hyödyntämisestä vaan katselimesta joka tekee täysin tavallisia asioita hyökkääjän toimittamalla syötteellä: avaa file://-linkin UNC-jaokseen joka vuotaa NTLM-tunnuksia, jättää väliaikaiskopion temp-hakemistoon, kopioi upotetut hyötykuormat minne tahansa tiedostonimerkkijono käskee. PDFium Component on lähdekoodinen PDF-katselin Delphille, C++Builderille ja Lazarukselle, ja se asettaa olennaiset kytkimet ulottuville: latausaikainen lippu joka tappaa skriptauksen, linkkiklikkaustapahtumat joita voit estää, liitteiden pääsy joka kulkee oman koodisi läpi, ja oikeusbittit jotka voit lukea. Järjestys alla seuraa dokumenttia hetkestä jolloin se laskeutuu hetkeen jolloin käyttäjä klikkaa jotakin siinä

Esikatseluruudun uhkamalli

Ole rehellinen siitä mitä "turvallinen esikatselu" sinulle tuo. Renderöijä jäsentää epäluotettavat tavut mitä tahansa teetkin, ja moottorin oma kovettaminen on lattia jolla seisot. Kaikki tuon lattian yläpuolella on sovelluspolitiikkaa: alustuvatko skriptit, mitä linkkiklikkaus tekee, pääsevätkö upotetut tiedostot levylle, ovatko leikepöytä ja tulostin ovia vai seiniä. Yksi asia joka kannattaa kirjoittaa pois aikaisin on moottorin FPDF_SetSandBoxPolicy-kytkin. Useimmat moottorirajoitukset on käännetty sisään, kytkin muuttaa käytännössä vähän, ja minkä tahansa eristystarinaosi budjetointi sille vain tuottaa vale tunnetta siitä että on tehty jotain. Kun syöte on aidosti vihamielinen, sano julkisen latausportaalin, ainoa todellinen eristys on renderöinti erillisessä matala-oikeus-prosessissa ja bitmapien laivaus käyttöliittymään. Prosessin sisäiset liput ovat politiikkaa. Ne eivät ole eristystä

PDFium Component -kaavio vertailee prosessin sisäisiä PDF-esikatselun käytäntökytkimiä ja prosessin ulkopuolista renderointia matalan oikeuden työläisessä vihamielisille asiakirjoille
Prosessinsisäiset liput nostavat kynnystä tunnetuille lähettäjille, kun taas anonyymit lataukset oikeuttavat erillisen vähäoikeuksisen työläisen, joka toimittaa vain bittikarttoja UI:lle

Kaksi pintaa on helppo unohtaa juuri siksi ettei yksikään klikkaus koske niitä. Ensimmäinen on väliaikaistiedostot. Jos putkesi väistää saapuvat dokumentit levylle ennen esikatselua, nuo väistetyt kopiot elävät istunnon yli ellei jotain todistettavasti poista niitä, ja tiedosto joka on "palautettavissa temp-hakemistosta" on hiljaa lyönyt jokaisen kontrollin jonka ruutu itse toteuttaa. Lataa muistista TPdfStreamAdapter:n kautta sen sijaan, jotta vihamieliset tavut eivät koskaan saa omaa polkuaan. Toinen on leikepöytä. Esikatselu joka sallii valitse-ja-kopioi on jo vienyt dokumentin ulos, yhden ruudullisen kerrallaan, eikä yksikään linkkisieppaus saa sitä kiinni

Tapa JavaScript latausaikana, ei käyttöliittymässä

Dokumentin JavaScript PDFium Componentissa alustuu vain yhdessä lomakkeentäyttöympäristön kanssa. Lataaminen FormFill := False:lla siis ottaa skriptauksen pois käytöstä juuresta sen oireiden tukahduttamisen sijaan:

procedure TPreviewPane.LoadUntrusted(const FilePath: string);
begin
  Pdf.FileName := FilePath;
  Pdf.FormFill := False;     // ei lomakeympäristöä, joten ei JavaScript-moottoria
  Pdf.Active := True;

  FPermissions := Pdf.Permissions;   // raaka lippusana; kaikki bitit asetettuina = rajoittamaton
end;

Kompromissi on todellinen ja kuuluu speciisi. Lomakkeen täytön ollessa pois käytöstä myös legitiimi AcroForm-vuorovaikutus ja validointiskriptit ovat poissa; kentät renderöityvät viimeisellä tallennetulla ulkonäöllä mutta niitä ei voi muokata. Esikatseluruudulle tuo on yleensä oikea valinta, koska esikatselu tarkoittaa katsoa, ei täyttää. Mutta jos sama ikkuna toimii myös lomakenttäpintana luotetuille sisäisille dokumenteille, vastaus on kaksi latauspolkua eksplisiittisellä luottamuspäätöksellä niiden välillä, ei yksi polku kompromissiasetuksella joka on liian löysä vihamieliseen tapaukseen ja liian tiukka luotettuun. Tuon jaon lomakenttäpuolella on omat ansansa, käsitelty lomakekentän navigoinnissa ja ulkonäön uudelleenluomisessa

Linkit: oletuskäsittelijä suoltaa ulos

Jätettyään yksin linkkiklikkaukset menevät suoraan käyttöjärjestelmälle. Katselimen oletus-LinkOptions sisältää loAutoOpenURI:n, mikä on file://-UNC-jaos-vuoto joka odottaa tapahtuvansa. Kaksi tapahtumaa muodostavat kuristuspisteen: OnWebLinkClick sivutekstissä havaituille URL-osoitteille, ja OnAnnotationLinkClick linkkimerkinnöille jotka kantavat URI- tai käynnistystoimintoja. Aseta Handled := True molemmissa, ehdottomasti, ennen kuin päätät mitään, sitten salli uudelleen vain mitä politiikka sallii. Toisena kerroksena, pudota loAutoOpenURI LinkOptions:ista vihamieliselle syötteelle ja varmista että loAutoLaunch, oletuksena pois, ei koskaan hiivi takaisin kopioidun konfiguraation kautta:

Virtauskaavio PDF-linkkiklikkauksen sieppaamisesta Delphi-esikatseluruudussa raakamerkkijonon järjestelmäetuliitteen tarkistuksella ja estettyjen linkkien auditointilokinnalla
Handled:n asettaminen molemmissa linkkitapahtumissa pitää jokaisen napsautuksen sovelluksen käytännön alla, ja etuliitetarkistus raakamerkkijonoon pitää file://- ja UNC-kaavat ulkopuolella
procedure TPreviewPane.PdfViewWebLinkClick(Sender: TObject;
  const Url: WString; var Handled: Boolean);
begin
  Handled := True;   // älä koskaan päästä suoritusta oletuskuoren toimintaan

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

Kaksi yksityiskohtaa päättävät pitääkö tämä oikeasti. Ensinnä, skeemitarkistuksen on oltava etuliitetarkistus raakalla merkkijonolla ennen mitään jäsentämistä, koska file://, UNC-polut ja eksoottiset skeemit ovat juuri ne arvot jotka kaatavat naiivin URL-jäsentimen tai lipsahtavat läpi sellaisen joka normalisoi liian innokkaasti. Toiseksi, lokita jokainen esto dokumenttitietoisuus kiinnitettynä. Kourallinen estettyjä file://-linkkejä on taustakohinaa; puhdistus niistä monen saapuvan dokumentin yli lyhyessä ikkunassa on incident jonka turvetiimisi mieluummin kuulisi sinulta kuin jostain muualta

Liitteet: laajennuspolitiikka ja tiedostonimi jota et valinnut

PDF on säiliö, ja AttachmentCount AttachmentName[]-ominaisuuden kanssa kertoo mitä se kantaa ennen kuin mikään koskettaa levyä. Kaksi erillistä kontrollia merkitsevät täällä, ja vain yksi niistä on ilmeinen. Ilmeinen on tyyppipolitiikka: sallittujen laajennusten allowlist jotka koskaan viedään ulos. Hienovaraisempi on, että liitteen nimi on hyökkääjän hallitsemaa dataa, piste. Upotettu nimi kuten ..\..\Startup\update.exe muuttaa huolimattoman tallennuksen polkukuljetukseksi joka pudottaa suoritettavan tiedoston kansioon jonka Windows ajaa sisäänkirjautuessa. Komponentti antaa hyötykuorman sinulle tavuina Attachment[]:n kautta ja antaa koodisi valita polun, joten rakenna tuo polku sanitetystä perusnimestä eikä koskaan raakasta upotetusta merkkijonosta:

PDFium Component -putkikaavio, joka puhdistaa hyökkääjän hallitseman PDF-liitenimen ExtractFileNamen ja tiedostopäätteen sallimistolistan kautta ennen tavujen kirjoittamista
Upotetun liitteen nimi on hyökkääjän syötettä, joten vientipolku rakennetaan uudelleen siivotusta perusnimestä ja portitetaan sulkevalla laajennusten sallituslistalla
procedure TPreviewPane.ExportAttachment(Index: Integer; const TargetDir: string);
var
  RawName, SafeName, Ext: string;
  Data: TBytes;
begin
  RawName := string(Pdf.AttachmentName[Index]);
  SafeName := ExtractFileName(RawName);    // poistaa kaikki polkuosat
  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];           // upotettu sisältö raakatavuina
  TFile.WriteAllBytes(
    IncludeTrailingPathDelimiter(TargetDir) + SafeName, Data);
end;

Suo allowlist-suuntaa. Vaarallisten laajennusten blocklist on kilpa jonka häviät päivänä jolloin joku aseistaa laajennuksen josta et ole koskaan kuullut; .pdf:n, .png:n ja .csv:n allowlist epäonnistuu suljettuna

Mitä salauksen oikeudet oikeasti lupaavat

ISO 32000-1:n standarditurvakäsittelijä koodaa oikeusliput tulostukselle, sisällön kopioinnille ja muokkaukselle, ja Permissions- ja UserPermissions-ominaisuudet tuovat ne esiin raakoina bitmaskieina kun dokumentti avautuu. ISO 32000-1 Taulukko 22 määrittelee bitit, ja salaukseton tiedosto raportoi jokaisen bitin asetettuna. Lue ne ja kunnioita niitä komentokerroksessasi, mutta ole selvä siitä mitä ne ovat. Dokumentille joka on salattu omistajasalasanalla ja tyhjällä käyttäjäsalasanalla, sisältö dekryptoituu täysin avauksessa, ja liput ovat pyyntö conforming-katselimille, ei toteutusmekanismi. Siinä on kaksi seurausta, ja ne vetävät vastakkaisiin suuntiin. Älä koskaan esitä oikeuslippuja käyttäjille turvaominaisuutena dokumenteille jotka he vastaanottavat, koska ne eivät ole sellaisia. Samalla kunnioita saavutettavuus-poimintabittiä (bitti 10) silloinkin kun yleinen kopiointi (bitti 5) on kielletty; ruudunlukuohjelman pääsy on kaiverrettu erikseen oikeusmalliin tahallaan, ja sen riisuminen siksi että "kopiointi on pois" rikkoo avustavan teknologian ilman turvahyötyä

Toteuta kielletyt toiminnot komentotasolla, ei työkalurivin painikkeiden piilottamisella. Ctrl+C, kontekstivalikot ja raahaa-valitse kaikki ohittavat työkalurivin; yksittäinen oikeustarkistus kopioinnin komenteen sisällä ei ohita mitään

Dokumenteille jotka oikeasti vaativat käyttäjäsalasanan, aseta Password ennen Active := True:a ja kohtele arvoa salaisuutena joka se on: nouda se tunnusseslistäsi per istunto, pidä se pois lokeista ja kaatumisraporteista, eikä koskaan pysäytä sitä dokumentin viereen. Esikatseluruutu joka välimuistittaa salasanat "mukavuuden vuoksi" on hiljaa muuttunut salasanatietokannaksi ilman yhtäkään sen suojista

Tulostus ansaitsee oman päätöksen ennemmin kuin perii minkä tahansa mihin kopiointisääntö laskeutui. Fyysinen tuloste on määritelmään kuuluvasti auditointimaton, mutta tulostuksen estäminen kokonaan taputtaa käyttäjiä kohti ruutukaappauksia, jotka ovat huonompia jokaisella akselilla. Yleinen välimaasto on sallia tulostus mutta leimata kukin sivu käyttäjän identiteetillä ja aikaleimalla, toteutettuna tulostuskomennon sisällä. Pidä vain oikea odotus siitä: vesileima on pelote ja attribuutio. Se ei ole esto

Mitä sisäänoton olisi pitänyt jo kertoa sinulle

Esikatseluruutu tekee parempia päätöksiä kun tiedosto saapuu jo kiinnitetyllä dosierilla: salattu tai ei, JavaScript läsnä tai poissa, liitelaskenta, lomaketyyppi. Tuo tarkistusläpikäynti kuuluu katselimen ylävirtaan, ja PDF-sisäänoton tarkistusworkbenchin rakentamisen malli tuottaa tarkalleen liput joita esikatselupolitiikka haluaa kuluttaa. Tiedostot jotka sisäänotto merkitsi riskialttiiksi avautuvat kovennetun polun kautta automaattisesti; rutiinidokumentit pitävät mukavuutensa. Sido kaksi vaihetta yhteen jaettuun politiikkaobjektiin ennemmin kuin kahteen konfiguraationäyttöön, jotka ajautuvat erilleen toisessa julkaisussa oli kuinka varovainen tahansa ensimmäisellä kerralla

Missä raja käy prosessin-sisäisen ja prosessin-ulkoisen välillä riippuu siitä kuka lähettää sinulle tiedostoja. Tavalliseen liiketoiminnan sisäänottoon, ihmiset jotka lähettävät dokumentteja ovat tunnettuja ja vain huolimattomia, ja prosessin-sisäinen esikatselu skriptaus pois ja linkit siepattuna on puolustettava rimaa. Anonyymeille julkisille latauksille se ei ole, eikä mikään määrä prosessin-sisäistä lipun-asetusta tee siitä sellaista; renderöi nuo erillisessä matala-oikeus-työntekijässä ja laivaa bitmapit käyttöliittymään, niin että moottorivika maksaa sinulle työntekijän ennemmin kuin isäntäsovelluksen. Päätä tuo jako tahallisesti ja kirjoita ylös mihin kaukaloon kukin sisäänottopolku kuuluu, koska väärin arvaamisen kustannus on epäsymmetrinen

Lisensöinti, turvaan liittyvä API-pinta ja kovennettu-katselin-demo ovat tuotesivulla: PDFium Component