Een onvertrouwd PDF-document vooraf bekijken binnen uw eigen applicatie is een uitvoeringsbeslissing, en wat telt is niet de buitenkant van de viewer maar wat het paneel weigert uit zichzelf te doen. Schrijf het bestand niet naar schijf. Laat de koppelingen niet naar de shell doorgeven. Geef de bijlagen geen pad. Het meeste schade van een vijandig document komt niet voort uit een engine-exploit maar uit een viewer die volkomen alledaagse dingen doet met input van een aanvaller: een file://-koppeling openen naar een UNC-share die NTLM-referenties lekt, een tijdelijke kopie in de temp-map achterlaten, ingebedde payloads kopiëren naar waar een bestandsnaamstring het zegt. PDFium Component is een source-code PDF-viewer voor Delphi, C++Builder en Lazarus, en plaatst de relevante schakelaars binnen bereik: een flag bij laden die scripting uitschakelt, koppelingsklik-events die u kunt veto'en, toegang tot bijlagen die via uw eigen code loopt, en permissiebits die u kunt uitlezen. De volgorde hieronder volgt een document vanaf het moment dat het binnenkomt tot het moment dat een gebruiker ergens in klikt
Het bedreigingsmodel van een voorbeeldvenster
Wees eerlijk over wat "veilige voorbeeldweergave" u oplevert. De renderer parseert onvertrouwde bytes ongeacht wat u doet, en de eigen hardening van de engine is de vloer waarop u staat. Alles daarboven is applicatiebeleid: of scripts initialiseren, wat een koppelingklik doet, of ingebedde bestanden schijf kunnen bereiken, of het klembord en de printer deuren of muren zijn. Eén ding om vroegtijdig af te schrijven is de FPDF_SetSandBoxPolicy-schakelaar van de engine. De meeste enginebeperkingen zijn ingecompileerd, de schakelaar verandert in de praktijk weinig, en een deel van uw isolatieverhaal op hem budgetteren levert alleen een vals gevoel op dat u iets gedaan hebt. Als de input werkelijk vijandig is, zeg een openbaar uploadportaal, dan is de enige echte isolatie renderen in een apart proces met lage privileges en bitmaps naar de UI sturen. In-process flags zijn beleid. Ze zijn geen containment
Twee oppervlakken worden makkelijk vergeten, juist omdat er nooit op geklikt wordt. De eerste zijn tijdelijke bestanden. Als uw pijplijn inkomende documenten naar schijf wegschrijft vóór de voorbeeldweergave, overleven die tijdelijke kopieën de sessie tenzij iets ze verifieerbaar verwijdert, en een bestand dat "terug te halen is uit de temp-map" heeft stilletjes elke controle die het paneel zelf handhaaft ongedaan gemaakt. Laad in plaats daarvan vanuit het geheugen via TPdfStreamAdapter, zodat de vijandige bytes nooit hun eigen pad krijgen. De tweede is het klembord. Een voorbeeldweergave die selecteren-en-kopiëren toestaat, heeft het document al geëxporteerd, één scherm tegelijk, en geen koppelingsonderschepping vangt dat
Kill JavaScript bij laden, niet in de UI
Document-JavaScript in PDFium Component initialiseert uitsluitend samen met de form-fill-omgeving. Laden met FormFill := False schakelt scripting daarom bij de wortel uit in plaats van de symptomen te onderdrukken:
procedure TPreviewPane.LoadUntrusted(const FilePath: string);
begin
Pdf.FileName := FilePath;
Pdf.FormFill := False; // geen form environment, dus ook geen JavaScript engine
Pdf.Active := True;
FPermissions := Pdf.Permissions; // rauw flagwoord; alle bits gezet = onbeperkt
end;
De trade-off is reëel en hoort in uw specificatie. Met form-fill uitgeschakeld zijn ook legitieme AcroForm-interactie en validatiescripts verdwenen; velden renderen met hun laatst opgeslagen appearance maar kunnen niet bewerkt worden. Voor een voorbeeldvenster is dat meestal de juiste keuze, want voorbeeldweergave betekent kijken, niet invullen. Maar als hetzelfde venster ook dienstdoet als invuloppervlak voor vertrouwde interne documenten, dan is het antwoord twee laadpaden met een expliciete vertrouwensbeslissing ertussen, niet één pad met een compromisinstelling die te los is voor het vijandige geval en te strak voor het vertrouwde. De invulkant van die splitsing heeft zijn eigen valstrikken, behandeld in navigatie en appearance-regeneratie van formuliervelden
Koppelingen: de standaardhandler shelt uit
Aan zichzelf overgelaten gaan koppelingsklikken rechtstreeks naar het besturingssysteem. De standaard LinkOptions van de viewer bevatten loAutoOpenURI, wat de file://-naar-UNC-share-lek is die staat te gebeuren. Twee events vormen het knooppunt: OnWebLinkClick voor URL's gedetecteerd in paginatekst, en OnAnnotationLinkClick voor koppelingsannotaties met URI- of launch-acties. Zet Handled := True in beide, onvoorwaardelijk, vóór u iets beslist, en sta daarna alleen toe wat het beleid toelaat. Voeg als tweede laag loAutoOpenURI toe aan de nee-zijde door het uit LinkOptions te verwijderen voor vijandige input en zorg dat loAutoLaunch, standaard uit, nooit via een gekopieerde configuratie terugsluipt:
procedure TPreviewPane.PdfViewWebLinkClick(Sender: TObject;
const Url: WString; var Handled: Boolean);
begin
Handled := True; // val nooit terug op het standaard shell-gedrag
if (AnsiStartsText('https://', Url) or AnsiStartsText('http://', Url))
and HostIsAllowed(Url) then
OpenInBrowser(Url)
else
FAudit.LogBlockedLink(FDocumentId, Url);
end;
Twee details bepalen of dit standhoudt. Ten eerste moet de scheme-check een prefix-check op de ruwe string zijn vóór enige parsing, omdat file://, UNC-paden en exotische schemes precies de waarden zijn die een naïeve URL-parser laten crashen of door een parser glijden die te ijverig normaliseert. Ten tweede, log elke blokkering met de documentidentiteit eraan vast. Een handvol geblokkeerde file://-koppelingen is achtergrondruis; een uitbarsting ervan over veel inkomende documenten in een kort venster is een incident dat uw beveiligingsteam liever van u hoort dan van ergens anders
Bijlagen: extensiebeleid en de bestandsnaam die u niet koos
Een PDF is een container, en AttachmentCount met de eigenschap AttachmentName[] vertelt u wat hij draagt vóór iets schijf raakt. Twee afzonderlijke controles tellen hier, en slechts één ervan is voor de hand liggend. De voor de hand liggende is typebeleid: een allowlist van extensies die ooit geëxporteerd mogen worden. De subtiele is dat de naam van de bijlage door de aanvaller gecontroleerde data is, punt uit. Een ingebedde naam als ..\..\Startup\update.exe verandert een onachtzaam opslaan in een path-traversal die een executable dropt in een map die Windows bij login uitvoert. De component levert de payload als bytes via Attachment[] en laat uw code het pad kiezen, dus bouw dat pad op uit een gesaneerde basename en nooit uit de ruwe ingebedde string:
procedure TPreviewPane.ExportAttachment(Index: Integer; const TargetDir: string);
var
RawName, SafeName, Ext: string;
Data: TBytes;
begin
RawName := string(Pdf.AttachmentName[Index]);
SafeName := ExtractFileName(RawName); // haalt eventuele padcomponenten eraf
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 als rauwe bytes
TFile.WriteAllBytes(
IncludeTrailingPathDelimiter(TargetDir) + SafeName, Data);
end;
Verkoos de allowlist-richting. Een blocklist van "gevaarlijke" extensies is een race die u verliest op de dag dat iemand een extensie waar u nooit van gehoord hebt wapent; een allowlist van .pdf, .png en .csv faalt dicht
Wat encryptiepermissies werkelijk beloven
De standaard security handler van ISO 32000-1 codeert permissieflags voor afdrukken, inhoud kopiëren en wijzigen, en de eigenschappen Permissions en UserPermissions tonen ze als ruwe bitmasks zodra het document opent. ISO 32000-1 Tabel 22 definieert de bits, en een niet-versleuteld bestand meldt elke bit gezet. Lees ze en eer ze in uw commandolaag, maar wees duidelijk over wat ze zijn. Voor een document versleuteld met een eigenaarswachtwoord en een leeg gebruikerswachtwoord ontcijfert de inhoud volledig bij openen, en de flags zijn een verzoek aan conforming viewers, geen afdwingmechanisme. Dat heeft twee consequenties, en ze trekken tegengesteld. Presenteer permissieflags nooit aan gebruikers als een beveiligingseigenschap van de documenten die ze ontvangen, want dat zijn ze niet. Eer tegelijkertijd de accessibility-extractiebit (bit 10) zelfs waar algemeen kopiëren (bit 5) geweigerd wordt; schermlezertoegang is in het permissiemodel met opzet apart uitgesneden, en het weghalen omdat "kopiëren uit staat" breekt hulptechnologie zonder beveiligingswinst
Dwing geweigerde acties af op commandoniveau, niet door werkbalkknoppen te verbergen. Ctrl+C, contextmenu's en drag-select omzeilen allemaal een werkbalk; een enkele permissiecheck binnen het kopieercommando omzeilt niets
Voor documenten die wel een gebruikerswachtwoord vereisen, wijs Password toe vóór Active := True en behandel de waarde als het geheim dat het is: haal het per sessie uit uw credential store, houd het uit logs en crashrapporten, en persisteer het nooit naast het document. Een voorbeeldvenster dat wachtwoorden "voor het gemak" cacheert is stilletjes een wachtwoorddatabank geworden zonder enige van de beschermingen daarvan
Afdrukken verdient een eigen beslissing in plaats van over te nemen wat de kopieerregel opleverde. Een fysieke afdruk is per definitie onge-audit, maar afdrukken blokkeren duwt gebruikers vaak naar screenshots, die op elke as slechter zijn. Een veelgehoord middenpad is afdrukken toestaan maar elke pagina stempelen met de identiteit van de gebruiker en een tijdstempel, afgedwongen binnen het afdrukcommando. Houd er wel de juiste verwachting op na: een watermerk is afschrikking en attributie. Het is geen preventie
Wat intake u al had moeten vertellen
Een voorbeeldvenster neemt betere beslissingen wanneer het bestand verschijnt met al een dossier eraan vast: versleuteld of niet, JavaScript aanwezig of afwezig, een bijlagentelling, het formtype. Die inspectiepass hoort upstream van de viewer, en het patroon in een PDF-intake-reviewwerkbank bouwen levert precies de flags die een voorbeeldbeleid wil consumeren. Bestanden die intake als riskant markeerde openen automatisch via het verharde pad; routine-documenten behouden hun gemakken. Koppel de twee fases aan één gedeeld beleidsobject in plaats van twee configuratieschermen, die bij de tweede release uit elkaar drijven hoe zorgvuldig u ze de eerste keer ook schrijft
Waar de grens tussen in-process en out-of-process valt hangt af van wie u bestanden stuurt. Voor gewone zakelijke intake zijn de mensen die documenten sturen bekend en slechts onachtzaam, en in-process voorbeeldweergave met scripting uit en koppelingen onderschept is een verdedigbare lat. Voor anonieme openbare uploads is dat niet zo, en geen enkele hoeveelheid in-process flag-instellen maakt het dat; render die in een aparte worker met lage privileges en stuur bitmaps naar de UI, zodat een enginefout u een worker kost in plaats van de hostapplicatie. Neem die splitsing bewust en schrijf op in welk pad elke intake-influx valt, omdat de kosten van verkeerd gokken asymmetrisch zijn
Licentiëring, het beveiligingsgerelateerde API-oppervlak en een hardened-viewer-demo staan op de productpagina: PDFium Component