Podpis nad PDF nezakazuje pozdější změny. Fixuje bajtový rozsah a inkrementální aktualizace po něm připojuje nové bajty, takže podpis zůstává matematicky platný, zatímco dokument získává nový obsah. Zda je tento obsah přijatelný, je otázka politiky a DocMDP je místo, kde autor politiku stanovuje: žádné změny vůbec, pouze vyplňování formulářů a podepisování, nebo to plus anotace. Vynucení znamená klasifikovat, co se skutečně změnilo, což je úkol AnalyzeModifications. Namiřte ji na dřívější revizi, poté si přečtěte GetModificationLevel pro celkový verdikt a přístupové funkce pro jednotlivá zjištění nesoucí úroveň, číslo objektu a popis každého rozdílu
S tím na místě se vynucení DocMDP sbalí na porovnání: je vypočtená úroveň na úrovni povolené politikou nebo pod ní
Proč se u podepsaného PDF očekává změna
Tři legitimní případy a pokrývají většinu toho, co uvidíte. Druhý podepisující přidává svůj podpis. Příjemce vyplňuje pole formulářů, které autor nechal otevřená. A materiál dlouhodobého ověřování se připojuje: odpovědi OCSP a CRL se zapisují do document security store dokumentu, takže podpis zůstává ověřitelný i po zmizení responderů. To poslední není jen povoleno, je to záměr dobře spravovaného archivu u podepsaných dokumentů
„Soubor po podpisu vyrostl“ tedy nenese žádnou informaci. Otázkou vždy je, co bylo přidáno, a odpověď musí přijít z porovnání stavů dokumentu, nikoli ze sledování bajtů. Mechanika připojování sama je předmětem článku o inkrementálních aktualizacích
Klasifikujte podle tvaru objektu, ne podle cesty, která jej vytvořila
Klasifikátor se dívá na to, čím objekt je po změně, nikoli na to, které volání knihovny jej vytvořilo. Je to záměrné, protože analýza běží nad soubory produkovanými jiným softwarem, kde žádná call path k prozkoumání není k dispozici
Rozpoznávají se čtyři tvary. Slovníky informací document security store a validace, objekty cross-reference stream, položka metadata katalogu a slovníky podpisů nesoucí bajtový rozsah jsou materiálem dlouhodobého archivu. Objekt nesoucí zároveň typ pole a hodnotu pole je vyplňování formulářů. Objekt, jehož typ je anotace, nebo jehož subtype je jedním z uvedených v Tabulce 168 normy ISO 32000-2, je změnou anotace. Všechno ostatní je neklasifikováno
Odstranění jsou posuzována přísněji než přídavky. Odstraněný objekt je whitelistován pouze tehdy, když objekt na staré straně byl sám archivním materiálem, což pokrývá běžný případ, kdy je security store nahrazen novějším. Každé jiné odstranění je neklasifikováno, protože mazání obsahu z podepsaného dokumentu není něco, co by úroveň oprávnění autorizovala. Rozdíly na úrovni dokumentu jsou ještě přísnější: změna počtu stránek jde rovnou do neklasifikováno bez zkoumání jednotlivých objektů, protože žádná úroveň DocMDP nepovoluje přidávání ani odebírání stránek
Whitelist se mýlí směrem k odmítání
Toto je návrhové pravidlo, které řídí každé hraniční rozhodnutí. Změna chybně klasifikovaná jako povolená je podpis validující se nad obsahem, který autor nikdy neautorizoval. Změna chybně klasifikovaná jako neklasifikovaná je dokument, který je označen a zkontrolován člověkem. Tyto dvě chyby nejsou symetrické, takže whitelist zůstává úzký a nerozpoznané tvary padají do neklasifikováno namísto dohadování
To má praktický důsledek, který stojí za předvídání: soubory od neobvyklých producentů budou občas hlásit neklasifikované změny, které jsou při prozkoumání neškodné. Správnou reakcí je podívat se na detail zjištění a číslo objektu, nikoli whitelist rozšiřovat, protože whitelist rostoucí, aby ztlumil jednotlivá hlášení, přestává být bezpečnostní kontrolou
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 v pořadí mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified; getter vrací svůj ordinál
Level := Pdf.GetModificationLevel;
// Vynucení DocMDP je nyní jediné porovnání s politikou
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;
Celková úroveň je maximum přes všechna zjištění, což je jediné obhajitelné agregování: dokument obsahující devadesát devět archivních přídavků a jednu neklasifikovanou změnu je neklasifikovaná změna
Pod kapotou: otisky prstů, ne kryptografické hashe
Srovnávací engine, který vystavuje CompareWith a na němž je analýza modifikací postavena, identifikuje objekty otiskem jejich normalizovaného těla pomocí nekryptografického 64bitového hashe namísto SHA-256. Je to promyšlená volba. Strukturální srovnání potřebuje determinismus: stejné tělo objektu musí v rámci běhu vždy produkovat stejný otisk. Nepotřebuje odolnost proti kolizím, protože útočník, který ovládá obě strany srovnání, už vyhrál jinými prostředky a platit za plný kryptografický hash nad každým objektem v dokumentu s milionem objektů je skutečná cena bez přínosu
Dvě normalizační pravidla jsou důležitější než volba hashe. Nepřímé reference se skládají na zástupný token namísto rozbalení do odkazovaného obsahu: rozbalení by zkopírovalo tělo sdíleného objektu do každého referrera, takže jedna malá úprava sdíleného popisovače písma by zneplatnila otisk každého objektu, který k němu dochází, a hlášení by bylo nečitelné. A samotná čísla objektů jsou z otisku vyloučena, protože přepis může objekty přečíslovat bez změny čehokoli sémantického
Porovnávání pak běží ve dvou průchodech, nejprve se zarovná podle otisku a zbytek se spáruje podle čísla objektu, aby se identifikovaly změny, nikoli přídavek plus odstranění. Levné kontroly přicházejí první v celém průběhu: rozdíl počtu stránek je nahlášen dříve, než začne jakékoli procházení objektů
Past: samo-srovnání není zaručeno být identické
Přirozený první test diff enginu je porovnat soubor se sebou samým a assertovat, že výsledek je identický. Tato aserce zde neplatí a důvod je poučný. Veřejná load cesta a cesta načítání dokumentu na nižší úrovni nekonfigurují dekódování identicky, takže tentýž soubor načtený oběma cestami může produkovat otisky, které se u některých objektů liší. Engine se nemýlí; obě načtení skutečně vytvořila různé stavy v paměti
Místo násilného spojení obou cest je sémantika srovnání stanovena úzce: analýza porovnává aktuální stav dokumentu s dřívější revizí a hlásí identické pouze tehdy, když se obě množiny otisků přesně kryjí. To je otázka, kterou si uživatelé skutečně kladou, a nevyžaduje, aby oba loadery byly zaměnitelné. Když navrhujete srovnávací funkci, definování významu „stejné“ je větší dílo než její výpočet
Kde to použít
Na dvou místech. V validačním hlášení, vedle kontroly podpisů, takže recenzent vidí nejen to, zda je podpis kryptograficky neporušený, ale i co se s dokumentem dělo poté; strana podpisů je předmětem podepisování a ověřování PAdES. A ve vstupní bráně, kde je dokument přicházející zvenčí kontrolován proti kopii, kterou jste odeslali, takže vrácená smlouva s přidanou anotací je posuzována jinak než ta s upravenou stránkou
Jedna výhrada k rozsahu. Tato analýza vám říká, co se změnilo mezi dvěma revizemi téže rodiny dokumentu. Neříká vám, zda je viditelný obsah klamavý, zda appearance stream pole formuláře odpovídá své hodnotě, nebo zda je text skrytý pod překryvem stále přítomen v content streamu. To vyžaduje samostatné zpracování a strana odstranění obsahu je předmětem článku o skutečné redakci. Vstupní body analýzy a srovnání jsou dokumentovány na stránce produktu losLab PDF Developer Library