En signatur over en PDF forbyder ikke senere ændringer. Den fastlægger et byte-interval, og en inkrementel opdatering tilføjer nye bytes efter det, så signaturen forbliver matematisk gyldig, mens dokumentet får nyt indhold. Om det indhold er acceptabelt er et politikspørgsmål, og DocMDP er der, hvor forfatteren slår politikken fast: slet ingen ændringer, kun formularudfyldelse og signering, eller det plus annoteringer. At håndhæve den betyder at klassificere, hvad der reelt ændredes, hvilket er det, AnalyzeModifications gør. Peg den på en tidligere revision, og aflæs derefter GetModificationLevel for den overordnede dom og de pr. fund gældende accessors for niveau, objektnummer og beskrivelse af hver forskel
Med det på plads kollapser DocMDP-håndhævelse til en sammenligning: er det beregnede niveau på eller under det niveau, politikken tillader
Hvorfor en signeret PDF forventes at ændre sig
Tre legitime tilfælde, og de dækker det meste af, hvad du vil se. En anden underskriver tilføjer sin signatur. En modtager udfylder formularfelter, som forfatteren lod stå åbne. Og langtidsvalideringsmateriale tilføjes: OCSP-svar og CRL'er skrevet ind i dokumentets security store, så signaturen forbliver verificerbar, efter at responderne er væk. Det sidste er ikke blot tilladt, det er, hvad et veldrevet arkiv med vilje gør ved signerede dokumenter
Så "filen voksede efter signering" bærer ingen information. Spørgsmålet er altid, hvad der blev tilføjet, og svaret må komme fra at sammenligne dokumenttilstande snarere end fra at iagttage bytes. Append-mekanikken selv er dækket i artiklen om inkrementelle opdateringer
Klassificér efter objektets form, ikke efter vejen, der producerede det
Klassifikatoren kigger på, hvad et objekt er efter ændringen, ikke på hvilket bibliotekskald, der skabte det. Det er bevidst, for analysen kører mod filer produceret af anden software, hvor der ikke er nogen kaldvej at inspicere
Fire former genkendes. Document security store og valideringsrelaterede informationsordbøger, cross-reference stream-objekter, catalogens metadata-post og signaturordbøger med et byte-interval er langtidsarkivmateriale. Et objekt, der bærer både en field type og en field value, er formularudfyldelse. Et objekt, hvis type er annotation, eller hvis subtype er en af dem, der er opført i ISO 32000-2 Table 168, er en annoteringsændring. Alt andet er unclassified
Fjernelser behandles mere strengt end tilføjelser. Et fjernet objekt whitelistes kun, når objektet på den gamle side selv var arkivmateriale, hvilket dækker det normale tilfælde, at en security store udskiftes med en nyere. Enhver anden fjernelse er unclassified, for at slette indhold fra et signeret dokument er ikke noget, et tilladelsesniveau autoriserer. Dokumentniveau-forskelle er endnu strengere: en ændring i sideantal går direkte til unclassified uden at undersøge enkelte objekter, da intet DocMDP-niveau tillader at tilføje eller fjerne sider
Whitelisten fejler hen imod at nægte
Dette er den designregel, der styrer enhver grænsetilfælde-beslutning. En ændring, der fejlagtigt klassificeres som tilladt, er en signatur, der validerer over indhold, som forfatteren aldrig autoriserede. En ændring, der fejlagtigt klassificeres som unclassified, er et dokument, der bliver flagget og gennemset af et menneske. De to fejl er ikke symmetriske, så whitelisten forbliver snæver, og ukendte former falder igennem til unclassified i stedet for at blive gættet
Det har en praktisk konsekvens, der er værd at forudse: filer fra usædvanlige producenter vil nogle gange rapportere unclassified-ændringer, der ved nærmere eftersyn er harmløse. Det rigtige svar er at se på funddetaljen og objektnummeret frem for at udvide whitelisten, for en whitelist, der vokser for at tilsløre enkelte rapporter, holder op med at være en sikkerhedskontrol
uses
PDFlibrary, PDFlibCompare;
var
Pdf: TPDFlib;
I, Level: Integer;
begin
Pdf := TPDFlib.Create(nil);
try
Pdf.LoadFromFile('contract-countersigned.pdf', '');
if Pdf.AnalyzeModifications('contract-as-signed.pdf', '') < 0 then
raise Exception.Create('the earlier revision could not be loaded');
// TPLModificationLevel ordnet mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified; getteren returnerer sin ordinal
Level := Pdf.GetModificationLevel;
// DocMDP-håndhævelse er nu én sammenligning mod politikken
if Level > Ord(mlFormFilling) then
for I := 0 to Pdf.GetModificationFindingCount - 1 do
Report.Add(Format('object %d, level %d: %s',
[Pdf.GetModificationFindingObjNum(I),
Pdf.GetModificationFindingLevel(I),
Pdf.GetModificationFindingDetail(I)]));
finally
Pdf.Free;
end;
end;
Det overordnede niveau er maksimum over alle fund, hvilket er den eneste forsvarlige aggregering: et dokument, der indeholder nioghalvfems arkivtilføjelser og én unclassified-ændring, er en unclassified-ændring
Under motorhjelmen: fingerprints, ikke kryptografiske hashes
Sammenligningsmotoren, som CompareWith eksponerer, og som ændringsanalysen er bygget på, identificerer objekter ved et fingerprint af deres normaliserede krop med en ikke-kryptografisk 64-bit hash frem for SHA-256. Det er et gennemtænkt valg. Det, strukturel sammenligning behøver, er determinisme: den samme objektkrop skal altid give det samme fingerprint inden for en kørsel. Den behøver ikke kollisionsmodstand, for en angriber, der styrer begge sider af sammenligningen, har allerede vundet ad andre veje, og at betale for en fuld kryptografisk hash over hvert objekt i et dokument med en million objekter er en reel omkostning uden gevinst
To normaliseringsregler betyder mere end valget af hash. Indirekte referencer foldes til en placeholder-token i stedet for at blive ekspanderet til det refererede indhold: ekspansion ville kopiere et delt objekts krop ind i hver refererer, så en lille redigering af en delt font descriptor ville invalidere fingerprintet af hvert objekt, der når frem til den, og rapporten ville være ulæselig. Og objektnumrene selv er udeladt af fingerprintet, for en omskrivning kan omnummerere objekter uden at ændre noget semantisk
Matching kører derefter i to gennemløb, først justering efter fingerprint og derefter parring af resten efter objektnummer for at identificere ændringer frem for en tilføjelse plus en fjernelse. Billige tjek kommer først hele vejen: en forskel i sideantal rapporteres, før nogen objektgennemløb begynder
En fælde: selvsammenligning er ikke garanteret identisk
Den naturlige første test for en diff-motor er at sammenligne en fil med sig selv og kræve, at resultatet er identisk. Den assertion holder ikke her, og årsagen er oplærrende. Den offentlige load-vej og den lavere niveaus dokument-load-vej konfigurerer ikke dekodning ens, så den samme fil indlæst gennem de to ruter kan give fingerprints, der afviger for enkelte objekter. Motoren tager ikke fejl; de to indlæsninger producerede genuint forskellige in-memory-tilstande
I stedet for at tvinge de to veje sammen er sammenligningsemantikken formuleret snævert: analysen sammenligner dokumentets nuværende tilstand med en tidligere revision og rapporterer identical kun, når de to fingerprint-sæt falder eksakt sammen. Det er det spørgsmål, brugere reelt stiller, og det kræver ikke, at de to loadere er udskiftelige. Når du designer en sammenligningsfunktion, er det at definere, hvad "det samme" betyder, en større del af arbejdet end at beregne det
Hvor man bruger det
To steder. I en valideringsrapport, ved siden af signaturtjek, så en gennemser ser ikke blot, om signaturen er kryptografisk intakt, men også, hvad der derefter hændte dokumentet; signaturensiden er dækket i PAdES-signering og -validering. Og i en indgangskontrol, hvor et dokument, der ankommer udefra, tjekkes mod den kopi, du sendte, så en returneret kontrakt med en tilføjet annotering behandles anderledes end én med en redigeret side
Ét forbehold omkring omfanget. Denne analyse fortæller dig, hvad der ændredes mellem to revisioner af den samme dokumentlinje. Den fortæller dig ikke, om synligt indhold er vildledende, om en form fields appearance stream matcher sin værdi, eller om tekst skjult under et overlay stadig findes i content stream. Det behøver separat behandling, og indholdsfjernelsesdelen af det er dækket i artiklen om ægte redaction. Analyse- og sammenlignings-entry points er dokumenteret på produktsiden losLab PDF Developer Library