PDF-tiedosto ei ole vain paperia. Se on säiliö, joka voi kuljettaa skriptejä, jotka suoritetaan tiedoston avautuessa, linkkejä, jotka käynnistävät ulkoisia ohjelmia, linkkejä, jotka ottavat yhteyttä verkkopalvelimiin, tiedostoja sisäkkäin tiedostojen sisällä sekä allekirjoituksen, joka vakuuttaa, ettei dokumentti ole muuttunut sen jälkeen, kun joku on sen taannut. Kun tiedosto saapuu lähteestä, jota et hallitse, turvallisin ensimmäinen siirto ei ole sen renderöiminen. Se on lukea, mitä tiedosto kertoo itsestään, ja rakentaa luettelo kaikesta, mitä se voisi yrittää tehdä, jotta ihminen voi päättää, kuuluuko se lainkaan työnkulkuusi.
Tässä artikkelissa käydään läpi staattinen, vain luku -tilan auditointivaihe kyseisen riskipinnan yli käyttäen PDFium-komponenttia Delphille ja Lazarukselle. Auditointi ei koskaan piirrä sivua. Se jäsentää dokumentin rakenteen, luettelee tiedoston osat, jotka sisältävät käyttäytymistä, ja kirjoittaa selkeän raportin. Se on kuin pyytäisi vierasta tyhjentämään taskunsa ovelle sen sijaan, että luottaisi häneen vain siksi, että hän hymyili.
Mitä auditointi on ja mitä se ei ole
Raja on syytä pitää selvänä. Hiekkalaatikossa toimiva esikatselu renderöi tiedoston tiukoin rajoituksin, jotta käyttäjä voi katsoa sitä ilman, että tiedosto koskettaa muuta konetta. Auditointi tulee ennen sitä. Se on renderöimätön tarkastus, jonka ainoa tulos on uhkapinnan kuvaus: mitä skriptejä on olemassa, mitkä toiminnot on kytketty linkkeihin, onko tiedosto allekirjoitettu ja kuinka tiukasti, ja mitä siihen on liitetty. Suoritat sen, kun dokumentti ylittää luottamusrajan, esimerkiksi sähköpostista, latauslomakkeesta tai kumppanin syötteestä saapuessa, ennen kuin mikään myöhempi vaihe avaa sen todellisuudessa.
Komponentti lataa dokumentin auditointia varten samalla tavalla kuin mihin tahansa muuhun tarkoitukseen. Asetat tiedostonimen ja aktivoit sen, mikä jäsentää ristiinviittaustiedot ja dokumenttiluettelon renderöimättä yhtäkään sivua. Kaikki alla oleva lukee tästä ladatusta, renderöimättömästä tilasta.
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'Incoming_Invoice.pdf';
Pdf.Active := True; // parses structure, renders nothing
// audit the loaded document here
finally
Pdf.Free;
end;
end;
Dokumenttitason JavaScript nimipuussa
Ensimmäinen lueteltava asia on koodi. PDF can carry document-level JavaScript: skriptejä, joita ei ole liitetty mihinkään sivuun tai kenttään vaan itse dokumenttiin, tallennettuna /Names-puuhun /JavaScript-merkinnän alle. Vaatimustenmukainen katseluohjelma ajaa nämä avattaessa. Se on mekanismi pitkän PDF-haittaohjelmien sarjan takana, koska se antaa tiedoston suorittaa logiikkaa heti, kun käyttäjä kaksoisnapsauttaa sitä, ennen kuin hän on lukenut sanaakaan.
Auditoija haluaa kaksi tietoa jokaisesta sellaisesta skriptistä: sen olemassaolon ja sen sisällön. Komponentti paljastaa määrän ja antaa sinun lukea kunkin toiminnon tietueena, joka sisältää skriptin nimen ja sen koko rungon. Rungon lukemisella on merkitystä. Skripti nimeltä Doc.0 ei kerro sinulle mitään, mutta sen teksti saattaa kutsua app.launchURL-funktiota tai koota merkkijonon ja välittää sen paikkaan, jonne sen ei pitäisi mennä. Lähdekoodin vetäminen ulos niin, että tarkastaja voi lukea sen, on koko idea tiedoston merkitsemisessä koodia ajavaksi avattaessa.
var
I: Integer;
Action: TPdfJavaScriptAction;
begin
if Pdf.JavaScriptActionCount > 0 then
WriteLn('WARNING: document runs ', Pdf.JavaScriptActionCount,
' script(s) on open');
for I := 0 to Pdf.JavaScriptActionCount - 1 do
begin
Action := Pdf.JavaScriptAction[I];
WriteLn(' script "', Action.Name, '":');
WriteLn(Action.Script); // full body, for a human to read
end;
end;
Tiedosto, jossa on nolla dokumenttiskriptiä, ei ole automaattisesti turvallinen, koska myös sivu- ja kenttäskriptejä on olemassa, mutta dokumenttiskriptejä sisältävä tiedosto ansaitsee aina toisen silmäyksen. Pelkkä esiintymismäärä on hyödyllinen portti, ja runko on se, mikä muuttaa portin tuomioksi.
Launch- ja URI-toiminnot
Seuraava kartoitettava käyttäytyminen elää linkeissä ja annotoinneissa. Kaksi toimintotyyppiä on auditoijalle tärkeintä. Launch-toiminto käynnistää ulkoisen ohjelman tai avaa paikallisen tiedoston, kun linkki laukaistaan. URI-toiminto avaa verkkokohteen. Epäilyttävää dokumenttia tarkastelevan arvioijan pitäisi pystyä näkemään klikkaamatta mitään, että sivun kolme painike on kytketty käynnistämään cmd.exe tai avaamaan URL-osoite, joka ei vastaa sivulla olevaa brändiä.
Komponentti luokittelee löytämänsä linkit ja paljastaa kunkin toimintotyypin ja kohdepolun, joten auditointi voi luetella jokaisen Launch- ja URI-toiminnon kohteineen. Tämä on raportointia, ei suorittamista. Auditoija lukee toiminnon rakenteesta ja kirjoittaa sen muistiin. Se ei koskaan seuraa sitä.
Katseluohjausobjekti, joka renderöi dokumentit, on paikka, jossa toiminnon seuraaminen tapahtuisi, ja sen oletusasento on tarkoituksella varovainen. TPdfView-ohjausobjektilla on LinkOptions-joukko, joka päättää, mitkä linkkityypit käynnistyvät automaattisesti napsautuksesta. Sen oletusarvo on [loAutoGoto, loAutoOpenURI], mikä tarkoittaa, että dokumentin sisäiset hypyt ja verkko-URL-osoitteet voivat avautua, mutta loAutoLaunch puuttuu, joten käynnistystoiminnot eivät koskaan suoritu automaattisesti. Auditointityönkulkua varten menet pidemmälle ja tyhjennät joukon kokonaan, jotta mikään ei käynnisty automaattisesti, kun olet vielä päättämässä, luotatko tiedostoon.
// Audit posture for the viewer: nothing auto-runs, nothing auto-opens.
View.LinkOptions := [];
// The shipped default already withholds launch:
// default = [loAutoGoto, loAutoOpenURI]
// loAutoLaunch is NOT in the default set, so external programs
// are never started on a stray click out of the box.
Perustelu käynnistyksen epäämiselle oletusarvoisesti on yksinkertainen. Hyppy dokumentin sisällä on vaaraton ja URL-osoite on näkyvä ja peruutettavissa, mutta mielivaltaisen ulkoisen ohjelman käynnistäminen napsautuksesta on vaarallisin asia, mitä PDF-linkki voi pyytää, joten se on pois päältä, ellet ota sitä käyttöön. Auditoija kieltäytyy jopa turvallisista toiminnoista, koska tehtävänä on katsoa, ei toimia.
Digitaalisen allekirjoituksen MDP-oikeustaso
Allekirjoitukset muuttavat kysymystä. Tavallinen allekirjoitus todistaa tavut allekirjoitushetkellä. Sertifiointiallekirjoitus, joka on luotu dokumentin muutosten havaitsemisen ja estämisen säännöllä, menee pidemmälle: se ilmoittaa, mikä voi laillisesti muuttua sen jälkeen, kun dokumentti on sertifioitu, ja vaatimustenmukainen katseluohjelma varoittaa, jos jotain sallitun ulkopuolella olevaa on kosketettu. Kyseisen oikeustason lukeminen kertoo auditoijalle, onko tiedosto sertifioitu ja jos on, kuinka tiukasti sen on tarkoitus olla lukittu.
MDP-oikeus on kokonaisluku, jolla on kolme määriteltyä arvoa. Taso 1 tarkoittaa, ettei mitään muutoksia sallita lainkaan; mikä tahansa muutos rikkoo sertifioinnin. Taso 2 sallii lomakkeen täytön ja allekirjoittamisen, mikä on tavallinen tapaus sopimukselle, joka on tarkoitus täyttää ja allekirjoittaa, mutta jota ei muutoin saa muuttaa. Taso 3 sallii lisäksi annotoinnit lomakkeen täytön ja allekirjoittamisen ohella. Oikeustason tietäminen auttaa vastaanottologiikkaasi päättelemään tarkoituksen: tasolla 1 sertifioitu tiedosto, joka kuitenkin sisältää lomakekenttiä tai skriptejä, on ristiriidassa itsensä kanssa, ja tämä ristiriita on syytä liputtaa.
Komponentti lukee allekirjoitusten määrän ja paljastaa kunkin tietueena, jonka Permission-kenttä kantaa kyseistä MDP-arvoa, joka on saatu suoraan taustalla olevasta FPDFSignatureObj_GetDocMDPPermission-kutsusta. Nollaoikeus tarkoittaa, ettei allekirjoitus ole sertifiointiallekirjoitus (DocMDP), joten dokumenttitason lukitusta ei ole raportoitavana.
var
I: Integer;
Sig: TPdfSignature;
begin
if Pdf.SignatureCount = 0 then
WriteLn('document is not signed')
else
for I := 0 to Pdf.SignatureCount - 1 do
begin
Sig := Pdf.Signature[I];
case Sig.Permission of
1: WriteLn('certified: no changes allowed');
2: WriteLn('certified: form fill and signing allowed');
3: WriteLn('certified: form fill, signing and annotations allowed');
else
WriteLn('signed, but not a DocMDP certification');
end;
end;
end;
Auditointi ei validoi tässä allekirjoituksen kryptografiaa; varmennusketjun tarkistaminen on erillinen asia. Se, mitä se raportoi, on ilmoitettu tarkoitus: tämä tiedosto sanoo olleensa lukittu tällä tasolla. Se on juuri se asiayhteys, jonka arvioija tarvitsee tuomitakseen, ovatko myöhemmät muutokset tai pelkkä aktiivisen sisällön läsnäolo sopusoinnussa sen kanssa, miten tekijä sulki dokumentin.
Muut osat uhkapinnasta: upotetut tiedostot ja XFA
Kaksi muuta kohdetta viimeistelevät täydellisen luettelon. Upotetut tiedostot ovat kokonaisia dokumentteja, joita kuljetetaan PDF-tiedoston sisällä liitteinä, ja ne ovat klassinen jakeluväline, koska vaarattomalta näyttävä raportti voi kuljettaa suoritettavaa tiedostoa tai toista haitallista PDF-tiedostoa liitepuussaan. Komponentti paljastaa liitteiden määrän ja kunkin liitteen nimen, joten auditointi voi luetella, mitä matkustaa mukana purkamatta tai avaamatta mitään siitä.
XFA:n läsnäolo on toinen lippu. XFA-lomake korvaa staattisen AcroForm-lomakkeen XML-pohjaisella lomakearkkitehtuurilla, joka tuo mukanaan oman renderöinti- ja skriptausmallinsa – laajemman ja monimutkaisemman pinnan kuin tavallinen lomake. Sinun ei tarvitse käsitellä XFA-tietoa huomataksesi, että se on siellä; sen pelkkä läsnäolo on signaali siitä, että tiedosto kantaa rikkaampaa vuorovaikutteista tasoa, joka ansaitsee tarkemman tarkastelun. Komponentti ilmoittaa sen yhdellä boolean-arvolla.
var
I: Integer;
begin
if Pdf.XFA then
WriteLn('NOTE: document contains an XFA form layer');
if Pdf.AttachmentCount > 0 then
begin
WriteLn('embedded files: ', Pdf.AttachmentCount);
for I := 0 to Pdf.AttachmentCount - 1 do
WriteLn(' - ', Pdf.AttachmentName[I]);
end;
end;
Yksi vain luku -rutiini, joka kirjoittaa raportin
Kokoa osat yhteen, ja auditointi on yksittäinen aliohjelma, joka lataa dokumentin, luettelee sen skriptit ja niiden rungot, luettelee Launch- ja URI-kohteet, raportoi allekirjoituksen MDP-tason, panee merkille liitteet ja XFA:n sekä kirjoittaa havainnot lokiin. Se ei renderöi mitään, joten se on halpa eikä sitä voi huijata näyttämään vihamielistä sivun sisältöä. Tuloksena on litteä, ihmisen luettavissa oleva tietue, jonka perusteella tarkastaja tai loppupään sääntö voi toimia.
Käytännössä hyvin toimiva muoto on kerätä jokainen havainto riviksi, varustaa aidosti riskialttiit etuliitteellä, jotta ne lajittuvat tarkastusjonon kärkeen, ja tallentaa koko tiedosto tiedoston viereen. Dokumentti, jossa ei ole skriptejä, ei käynnistystoimintoja, ei liitteitä, ei XFA:ta ja joko ei allekirjoitusta tai johdonmukainen sertifiointi, läpäisee tarkastuksen hiljaa. Dokumentti, joka laukaisee useita lippuja kerralla, on se, jonka ihmisen pitäisi nähdä ennen kuin mikään myöhempi vaihe avaa sen. Auditointi ei tee luottamuspäätöstä puolestasi. Se varmistaa, että päätös on tietoinen eikä sokea.
Kun tiedosto on selvittänyt auditoinnin ja sinun täytyy katsoa sitä, tee se rajoitusten alaisena mieluummin kuin oletuskatseluohjelmassa. Lähestymistapa artikkelissamme turvallisen PDF-esikatselun rakentamisesta Delphissä näyttää, miten linkkien automaattinen käsittely ja aktiivinen sisältö pidetään toimettomana hallitun katselun aikana. Tämän luetteloinnin liittämisestä täydelliseen vastaanottoputkeen tarkastustyökaluineen on tietoa PDF-vastaanoton ja tarkastuksen työpöydän artikkelissa. Molemmat rakentuvat samalle vain luku -, renderöimättömälle perustalle ja toimitetaan osana Delphin ja C++Builderin PDFium Componentia renderöinti-, teksti-, lomake- ja allekirjoitus-API:en rinnalla, joita käsitellään muualla tässä blogissa.