En signatur över en PDF förbjuder inte senare ändringar. Den fixerar ett byteintervall, och en inkrementell uppdatering lägger till nya byte efter den, så signaturen förblir matematiskt giltig medan dokumentet får nytt innehåll. Huruvida det innehållet är acceptabelt är en policyfråga, och DocMDP är där upphovspersonen anger policyn: inga ändringar alls, enbart formulärifyllning och signering, eller det plus anteckningar. Att tillämpa den betyder att klassificera vad som faktiskt ändrats, vilket är vad AnalyzeModifications gör. Rikta den mot en tidigare revision och läs sedan GetModificationLevel för den övergripande bedömningen och de per-fynd-accessorer som ger nivå, objektnummer och beskrivning för varje skillnad
Med det på plats kollapsar DocMDP-tillämpningen till en jämförelse: är den beräknade nivån på eller under den nivå policyn tillåter
Varför en signerad PDF förväntas ändras
Tre legitima fall, och de täcker det mesta du kommer att se. En andra undertecknare lägger till sin signatur. En mottagare fyller i formulärfält som upphovspersonen lämnat öppna. Och material för långtidsvalidering läggs till: OCSP-svar och CRL:er skrivna in i dokumentets security store så att signaturen förblir verifierbar när respondrarna är borta. Det sista är inte bara tillåtet, det är vad ett välhanterat arkiv gör med signerade dokument med flit
Så ”filen växte efter signering” bär ingen information. Frågan är alltid vad som lades till, och svaret måste komma från att jämföra dokumenttillstånd i stället för från att iaktta byte. Appendmekaniken själv tas upp i artikeln om inkrementella uppdateringar
Klassificera efter objektets form, inte efter vägen som skapade det
Klassificeraren tittar på vad ett objekt är efter ändringen, inte på vilket biblioteksanrop som skapade det. Det är medvetet, eftersom analysen körs mot filer producerade av annan programvara, där ingen anropsväg finns att inspektera
Fyra former känns igen. Information dictionaries för document security store och valideringsrelaterad information, cross-reference-strömobjekt, katalogens metadatapost och signaturordlistor med ett byteintervall är material för långtidsarkiv. Ett objekt som bär både en fälttyp och ett fältvärde är formulärifyllning. Ett objekt vars typ är annotation, eller vars subtype är en av dem som listas i ISO 32000-2 tabell 168, är en anteckningsändring. Allt annat är oklassificerat
Borttagningar behandlas strängare än tillägg. Ett borttaget objekt vitlistas bara när objektet på den gamla sidan själv var arkivmaterial, vilket täcker det normala fallet att en security store ersätts av en nyare. Varje annan borttagning är oklassificerad, för att ta bort innehåll ur ett signerat dokument är inget som en behörighetsnivå auktoriserar. Skillnader på dokumentnivå är ännu strängare: en ändring i sidantal går direkt till oklassificerad utan att enskilda objekt granskas, eftersom ingen DocMDP-nivå tillåter att sidor läggs till eller tas bort
Vitlistan tenderar att vägra
Det är designregeln som styr varje gränsfall. En ändring som felaktigt klassificeras som tillåten är en signatur som validerar över innehåll upphovspersonen aldrig auktoriserade. En ändring som felaktigt klassificeras som oklassificerad är ett dokument som flaggas och granskas av en människa. Dessa två fel är inte symmetriska, så vitlistan hålls smal och okända former faller igenom till oklassificerad i stället för att gissas fram
Det har en praktisk konsekvens värd att förutse: filer från ovanliga producenter kommer ibland att rapportera oklassificerade ändringar som, vid granskning, är harmlösa. Det rätta svaret är att titta på fynddetaljen och objektnumret i stället för att bredda vitlistan, för en vitlista som växer för att tysta enskilda rapporter slutar vara en säkerhetskontroll
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 ordnad mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified; gettern returnerar dess ordinal
Level := Pdf.GetModificationLevel;
// DocMDP-tillämpning är nu en enda jämförelse mot policyn
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;
Den övergripande nivån är maximum över alla fynd, vilket är den enda försvarbara aggregeringen: ett dokument som innehåller nittinio arkivtillägg och en oklassificerad ändring är en oklassificerad ändring
Under huven: fingeravtryck, inte kryptografiska hashar
Jämförelsemotorn som CompareWith exponerar, och som ändringsanalysen är byggd på, identifierar objekt med ett fingeravtryck av sin normaliserade kropp med en icke-kryptografisk 64-bit hash i stället för SHA-256. Det är ett genomtänkt val. Vad strukturell jämförelse behöver är determinism: samma objektkropp måste alltid ge samma fingeravtryck inom en körning. Den behöver inte kollisionsmotstånd, för en angripare som kontrollerar båda sidorna av jämförelsen har redan vunnit med andra medel, och att betala för en fullständig kryptografisk hash över varje objekt i ett dokument med en miljon objekt är en verklig kostnad utan nytta
Två normaliseringsregler betyder mer än hashvalet. Indirekta referenser viks till en platshållartoken i stället för att expanderas till det refererade innehållet: expansion skulle kopiera ett delat objekts kropp till varje refererare, så en liten redigering av en delad teckensnittsbeskrivare skulle ogiltigförklara fingeravtrycket för varje objekt som når den, och rapporten skulle vara oläsbar. Och objektnumren själva exkluderas från fingeravtrycket, eftersom en omskrivning kan omnumrera objekt utan att ändra något semantiskt
Matchningen körs sedan i två pass, först inriktning efter fingeravtryck och sedan parning av resten efter objektnummer för att identifiera ändringar i stället för ett tillägg plus en borttagning. Billiga kontroller kommer först genomgående: en skillnad i sidantal rapporteras innan någon objektgenomgång börjar
En fälla: självjämförelse är inte garanterat identisk
Det naturliga första testet för en diffmotor är att jämföra en fil med sig själv och hävda att resultatet är identiskt. Det antagandet håller inte här, och orsaken är lärorik. Det publika inläsningsspåret och den lägre nivåns dokumentinläsningsspår konfigurerar inte avkodningen identiskt, så samma fil inläst genom de två vägarna kan ge fingeravtryck som skiljer sig för vissa objekt. Motorn är inte fel; de två inläsningarna producerade genuint olika tillstånd i minnet
I stället för att tvinga ihop de två spåren anges jämförelsesemantiken smalt: analysen jämför dokumentets aktuella tillstånd mot en tidigare revision och rapporterar identisk bara när de två fingeravtrycksuppsättningarna sammanfaller exakt. Det är frågan användare faktiskt ställer, och den kräver inte att de två inläsarna är utbytbara. När du designar en jämförelsefunktion är att definiera vad ”detsamma” betyder mer av arbetet än att beräkna det
Var man använder det
Två platser. I en valideringsrapport, jämte signaturkontroll, så att en granskare ser inte bara om signaturen är kryptografiskt intakt utan vad som hände med dokumentet därefter; signatursidan tas upp i PAdES-signering och validering. Och i en intagsgrind, där ett dokument som anländer utifrån kontrolleras mot kopian du skickade, så att ett returnerat avtal med en tillagd anteckning behandlas annorlunda än ett med en redigerad sida
En brasklapp om omfattning. Denna analys talar om för dig vad som ändrats mellan två revisioner av samma dokumentlinje. Den talar inte om för dig huruvida synligt innehåll är missvisande, huruvida en formulärfälts appearance-ström stämmer med sitt värde eller huruvida text gömd under ett overlay fortfarande finns i innehållsströmmen. Det behöver separat behandling, och innehållsborttagningssidan tas upp i artikeln om äkta svärtning. Analys- och jämförelseingångspunkterna är dokumenterade på produktsidan för losLab PDF Developer Library