Å forhåndsvise en ubetrodd PDF inne i din egen applikasjon er en beslutning om kjøring, og den delen som betyr noe, er ikke viserens innramming, men hva ruten nekter å gjøre på egen hånd. Ikke skriv filen til disk. Ikke la lenkene dens starte et shell. Ikke gi vedleggene dens en sti. Mesteparten av skaden fra et fiendtlig dokument kommer ikke fra en motor-utnyttelse, men fra en viser som gjør helt ordinære ting med input levert av en angriper: åpner en file://-lenke til en UNC-delt ressurs som lekker NTLM-legitimasjon, etterlater en mellomlagret kopi i temp-mappen, kopierer innebygde nyttelaster dit en filnavnstreng ber den om. PDFium Component er en kildekodebasert PDF-viser for Delphi, C++Builder og Lazarus, og den plasserer de relevante bryterne der du kan nå dem: et flagg på lastetidspunktet som dreper skripting, lenkeklikk-hendelser du kan nedlegge veto mot, vedleggstilgang som går gjennom din egen kode, og tillatelsesbiter du kan lese. Rekkefølgen nedenfor følger et dokument fra øyeblikket det lander, til øyeblikket en bruker klikker på noe i det
Trusselmodellen til en forhåndsvisningsrute
Vær ærlig om hva "sikker forhåndsvisning" faktisk gir deg. Rendermotoren analyserer ubetrodde byte uansett hva du gjør, og motorens egen herding er gulvet du står på. Alt over det gulvet er applikasjonspolicy: om skript initialiseres, hva et lenkeklikk gjør, om innebygde filer kan nå disken, om utklippstavlen og skriveren er dører eller vegger. Én ting man tidlig bør avskrive, er motorens FPDF_SetSandBoxPolicy-bryter. De fleste motorbegrensninger er kompilert inn, bryteren endrer lite i praksis, og å budsjettere noen del av isolasjonshistorien sin til den, gir bare en falsk følelse av å ha gjort noe. Når input virkelig er fiendtlig, si en offentlig opplastingsportal, er den eneste ekte isolasjonen å rendre i en separat prosess med lave privilegier og sende bitmaps til brukergrensesnittet. In-process-flagg er policy. De er ikke innesluttet
To overflater er lette å glemme, nettopp fordi ingen klikk noensinne berører dem. Den første er midlertidige filer. Hvis pipelinen din mellomlagrer innkommende dokumenter på disk før forhåndsvisning, overlever de mellomlagrede kopiene økten med mindre noe påviselig sletter dem, og en fil som er "gjenopprettbar fra temp-mappen", har i stillhet nøytralisert enhver kontroll ruten selv håndhever. Last fra minnet gjennom TPdfStreamAdapter i stedet, slik at de fiendtlige bytene aldri får sin egen sti. Den andre er utklippstavlen. En forhåndsvisning som tillater merk-og-kopier, har allerede eksportert dokumentet, én skjermfull om gangen, og ingen lenkeavlytting vil fange det
Drep JavaScript ved lasting, ikke i brukergrensesnittet
Dokument-JavaScript i PDFium Component initialiseres bare sammen med skjemautfyllingsmiljøet. Å laste med FormFill := False deaktiverer derfor skripting ved roten i stedet for å undertrykke symptomene:
procedure TPreviewPane.LoadUntrusted(const FilePath: string);
begin
Pdf.FileName := FilePath;
Pdf.FormFill := False; // ingen skjemamiljø, dermed ingen JavaScript-motor
Pdf.Active := True;
FPermissions := Pdf.Permissions; // rått flaggord; alle biter satt = ubegrenset
end;
Avveiningen er reell og hører hjemme i spesifikasjonen din. Med skjemautfylling deaktivert er også legitim AcroForm-interaksjon og valideringsskript borte; felt rendres med sitt sist lagrede utseende, men kan ikke redigeres. For en forhåndsvisningsrute er det som regel riktig valg, siden forhåndsvisning betyr å se, ikke å fylle ut. Men hvis det samme vinduet også fungerer som en skjemautfyllingsflate for betrodde interne dokumenter, er svaret to lastestier med en eksplisitt tillitsbeslutning imellom, ikke én sti med en kompromissinnstilling som er for løs for det fiendtlige tilfellet og for stram for det betrodde. Skjemautfyllingssiden av det skillet har sine egne feller, dekket i navigasjon i skjemafelt og gjenoppbygging av utseende
Lenker: standardhåndtereren starter et shell
Overlatt til seg selv går lenkeklikk rett til operativsystemet. Viserens standard-LinkOptions inkluderer loAutoOpenURI, som er file://-til-UNC-delt-ressurs-lekkasjen som venter på å skje. To hendelser utgjør flaskehalsen: OnWebLinkClick for URL-er oppdaget i sidetekst, og OnAnnotationLinkClick for lenkeannotasjoner som bærer URI- eller launch-handlinger. Sett Handled := True i begge, betingelsesløst, før noe som helst avgjøres, og tillat deretter bare det policyen tillater. Som et andre lag, fjern loAutoOpenURI fra LinkOptions for fiendtlig input, og sørg for at loAutoLaunch, av som standard, aldri sniker seg tilbake gjennom en kopiert konfigurasjon:
procedure TPreviewPane.PdfViewWebLinkClick(Sender: TObject;
const Url: WString; var Handled: Boolean);
begin
Handled := True; // faller aldri gjennom til standard shell-oppførselen
if (AnsiStartsText('https://', Url) or AnsiStartsText('http://', Url))
and HostIsAllowed(Url) then
OpenInBrowser(Url)
else
FAudit.LogBlockedLink(FDocumentId, Url);
end;
To detaljer avgjør om dette faktisk holder. For det første må skjemakontrollen være en prefikskontroll på den rå strengen før noen parsing skjer, fordi file://, UNC-stier og eksotiske skjemaer er nettopp de verdiene som krasjer en naiv URL-parser eller glipper gjennom en som normaliserer for ivrig. For det andre, logg hver blokkering med dokumentidentiteten festet til. En håndfull blokkerte file://-lenker er bakgrunnsstøy; en byge av dem på tvers av mange innkommende dokumenter i et kort tidsvindu er en hendelse sikkerhetsteamet ditt heller vil høre om fra deg enn fra et annet sted
Vedlegg: filtypepolicy og filnavnet du ikke valgte
En PDF er en beholder, og AttachmentCount sammen med AttachmentName[]-egenskapen forteller deg hva den bærer, før noe berører disken. To separate kontroller betyr noe her, og bare én av dem er opplagt. Den opplagte er typepolicy: en tillatelsesliste over filtyper som noensinne kan eksporteres. Den subtile er at vedleggets navn er data kontrollert av angriperen, punktum. Et innebygd navn som ..\..\Startup\update.exe gjør et uforsiktig lagre-kall til en stitraversering som slipper en kjørbar fil inn i en mappe Windows kjører ved innlogging. Komponenten gir deg nyttelasten som byte gjennom Attachment[] og lar koden din velge stien, så bygg den stien fra et sanert basisnavn og aldri fra den rå innebygde strengen:
procedure TPreviewPane.ExportAttachment(Index: Integer; const TargetDir: string);
var
RawName, SafeName, Ext: string;
Data: TBytes;
begin
RawName := string(Pdf.AttachmentName[Index]);
SafeName := ExtractFileName(RawName); // fjerner alle stikomponenter
Ext := LowerCase(ExtractFileExt(SafeName));
if not FAllowedExt.Contains(Ext) then // tillatelsesliste, ikke blokkeringsliste
raise EPreviewPolicy.CreateFmt('Attachment type %s blocked by policy', [Ext]);
Data := Pdf.Attachment[Index]; // innebygd nyttelast som rå byte
TFile.WriteAllBytes(
IncludeTrailingPathDelimiter(TargetDir) + SafeName, Data);
end;
Foretrekk tillatelseslisteretningen. En blokkeringsliste over "farlige" filtyper er et kappløp du taper den dagen noen bevæpner en filtype du aldri har hørt om; en tillatelsesliste med .pdf, .png og .csv feiler lukket
Hva krypteringstillatelser faktisk lover
Standardsikkerhetshåndtereren i ISO 32000-1 koder tillatelsesflagg for utskrift, innholdskopiering og endring, og egenskapene Permissions og UserPermissions eksponerer dem som rå bitmasker når dokumentet åpnes. ISO 32000-1 tabell 22 definerer bitene, og en ukryptert fil rapporterer hver bit satt. Les dem og respekter dem i kommandolaget ditt, men vær tydelig på hva de er. For et dokument kryptert med et eierpassord og et tomt brukerpassord dekrypteres innholdet fullt ut ved åpning, og flaggene er en forespørsel til samsvarende visere, ikke en håndhevingsmekanisme. Det har to konsekvenser, og de trekker i motsatte retninger. Presenter aldri tillatelsesflagg for brukere som en sikkerhetsegenskap ved dokumentene de mottar, fordi de ikke er det. Samtidig må man respektere tilgjengelighets-utrekkingsbiten (bit 10), selv der generell kopiering (bit 5) er nektet; skjermleser-tilgang er bevisst skilt ut separat i tillatelsesmodellen, og å fjerne den fordi "kopiering er av", ødelegger hjelpeteknologi uten noen sikkerhetsgevinst
Håndhev nektede handlinger på kommandonivå, ikke ved å skjule verktøylinjeknapper. Ctrl+C, kontekstmenyer og dra-merking omgår alle en verktøylinje; en enkelt tillatelseskontroll inne i kopikommandoen omgår ingenting
For dokumenter som faktisk krever et brukerpassord, tildel Password før Active := True, og behandle verdien som hemmeligheten den er: hent den fra legitimasjonslageret ditt per økt, hold den unna logger og krasjrapporter, og lagre den aldri sammen med dokumentet. En forhåndsvisningsrute som mellomlagrer passord "for enkelhets skyld", har i stillhet blitt en passorddatabase uten noen av beskyttelsene til en slik
Utskrift fortjener sin egen beslutning fremfor å arve hva kopieringsregelen endte opp med. En fysisk utskrift er per definisjon uovervåket, likevel har det å blokkere utskrift helt en tendens til å presse brukere mot skjermbilder, som er verre på alle akser. Et vanlig mellomstandpunkt er å tillate utskrift, men stemple hver side med brukerens identitet og et tidsstempel, håndhevet inne i utskriftskommandoen. Hold bare riktig forventning til det: et vannmerke er avskrekking og attribusjon. Det er ikke forebygging
Hva inntaket allerede burde ha fortalt deg
En forhåndsvisningsrute tar bedre beslutninger når filen dukker opp med et dossier allerede vedlagt: kryptert eller ikke, JavaScript til stede eller ikke, en vedleggstelling, skjematypen. Den inspeksjonsrunden hører hjemme oppstrøms for viseren, og mønsteret i bygging av en arbeidsstasjon for gjennomgang av innkommende PDF-er produserer nøyaktig de flaggene en forhåndsvisningspolicy ønsker å konsumere. Filer som inntaket merket som risikable, åpner automatisk gjennom den herdede stien; rutinemessige dokumenter beholder sine bekvemmeligheter. Bind de to trinnene til ett delt policyobjekt fremfor to konfigurasjonsskjermer, som vil skli fra hverandre innen andre utgivelse uansett hvor omhyggelig du skriver dem første gang
Hvor grensen faller mellom in-process og out-of-process, avhenger av hvem som sender deg filer. For vanlig forretningsinntak er personene som sender dokumenter, kjente og bare uforsiktige, og in-process forhåndsvisning med skripting av og lenker avlyttet er en forsvarlig standard. For anonyme offentlige opplastinger er den ikke det, og ingen mengde in-process-flaggsetting gjør den til det; rendre disse i en separat arbeider med lave privilegier og send bitmaps til brukergrensesnittet, slik at en motorfeil koster deg en arbeider i stedet for vertsapplikasjonen. Bestem det skillet bevisst, og skriv ned hvilken bøtte hver inntakssti faller i, fordi kostnaden ved å gjette feil er asymmetrisk
Lisensiering, den sikkerhetsrelaterte API-overflaten og en herdet-viser-demo finnes på produktsiden: PDFium Component