Eine Signatur über ein PDF verbietet spätere Änderungen nicht. Sie fixiert einen Bytebereich, und ein inkrementelles Update hängt neue Bytes dahinter an, sodass die Signatur mathematisch gültig bleibt, während das Dokument neuen Inhalt bekommt. Ob dieser Inhalt akzeptabel ist, ist eine Richtlinienfrage, und DocMDP ist der Ort, an dem der Autor die Richtlinie erklärt: gar keine Änderungen, nur Formularausfüllen und Signieren, oder beides plus Annotationen. Durchsetzen bedeutet, zu klassifizieren, was sich tatsächlich geändert hat, und genau das tut AnalyzeModifications. Richten Sie es auf eine frühere Revision, dann lesen Sie GetModificationLevel für das Gesamturteil und die Finding-Accessors für Level, Objektnummer und Beschreibung jeder Differenz
Steht das an Ort und Stelle, schrumpft die DocMDP-Durchsetzung auf einen Vergleich: Liegt der berechnete Level auf oder unter dem Level, den die Richtlinie erlaubt
Warum von einem signierten PDF erwartet wird, dass es sich ändert
Drei legitime Fälle, und sie decken das meiste ab, was Ihnen begegnen wird. Ein zweiter Unterzeichner fügt seine Signatur hinzu. Ein Empfänger füllt Formularfelder aus, die der Autor offen gelassen hat. Und Langzeit-Validierungsmaterial wird angehängt: OCSP-Antworten und CRLs, geschrieben in den Document Security Store, damit die Signatur auch nach dem Verschwinden der Responder verifizierbar bleibt. Letzteres ist nicht bloß erlaubt, es ist das, was ein gut geführtes Archiv mit signierten Dokumenten gezielt tut
„Die Datei ist nach dem Signieren gewachsen“ trägt daher keine Information. Die Frage lautet immer, was hinzugekommen ist, und die Antwort muss aus dem Vergleich der Dokumentzustände stammen, nicht aus dem Beobachten von Bytes. Die Append-Mechanik selbst behandelt der Artikel zu inkrementellen Updates
Klassifizieren nach der Form des Objekts, nicht nach dem Pfad, der es erzeugte
Der Klassifizierer schaut darauf, was ein Objekt nach der Änderung ist, nicht darauf, welcher Bibliotheksaufruf es erzeugte. Das ist Absicht, denn die Analyse läuft gegen Dateien, die andere Software erzeugt hat, wo kein Aufrufpfad zum Inspizieren vorhanden ist
Vier Formen werden erkannt. Document-Security-Store- und validierungsbezogene Informations-Dictionaries, Cross-Reference-Stream-Objekte, der Catalog-Metadata-Eintrag und Signatur-Dictionaries mit Bytebereich sind Langzeitarchiv-Material. Ein Objekt, das sowohl einen Feldtyp als auch einen Feldwert trägt, ist Formularausfüllen. Ein Objekt, dessen Typ annotation ist oder dessen Subtype einer der in ISO 32000-2 Tabelle 168 gelisteten ist, ist eine Annotationsänderung. Alles andere ist unklassifiziert
Entfernungen werden strenger behandelt als Ergänzungen. Ein entferntes Objekt wird nur dann gewhitelistet, wenn das Objekt auf der alten Seite selbst Archivmaterial war, was den Normalfall eines Security Stores abdeckt, der von einem neueren abgelöst wird. Jede andere Entfernung ist unklassifiziert, denn das Löschen von Inhalt aus einem signierten Dokument ist nichts, was eine Berechtigungsstufe autorisiert. Dokumentebenen-Differenzen sind noch strenger: Eine Änderung der Seitenzahl geht direkt zu unklassifiziert, ohne einzelne Objekte zu untersuchen, da kein DocMDP-Level das Hinzufügen oder Entfernen von Seiten erlaubt
Die Whitelist irrt in Richtung Ablehnen
Das ist die Designregel, die jede Grenzentscheidung regiert. Eine Änderung, fälschlich als erlaubt klassifiziert, ist eine Signatur, die über Inhalt validiert, den der Autor nie autorisiert hat. Eine Änderung, fälschlich als unklassifiziert eingestuft, ist ein Dokument, das markiert und von einem Menschen geprüft wird. Diese beiden Fehler sind nicht symmetrisch, also bleibt die Whitelist eng, und unerkannte Formen fallen durch zu unklassifiziert, statt geraten zu werden
Das hat eine praktische Konsequenz, die man vorwegnehmen sollte: Dateien von ungewöhnlichen Producern melden manchmal unklassifizierte Änderungen, die sich bei Inspektion als harmlos erweisen. Die richtige Reaktion ist, auf das Finding-Detail und die Objektnummer zu schauen, statt die Whitelist zu verbreitern, denn eine Whitelist, die wächst, um einzelne Meldungen zum Verstummen zu bringen, hört auf, eine Sicherheitskontrolle zu sein
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 geordnet als mlNone, mlLTAUpdates, mlFormFilling,
// mlAnnotations, mlUnclassified; der Getter liefert den Ordinalwert
Level := Pdf.GetModificationLevel;
// DocMDP-Durchsetzung ist jetzt ein einziger Vergleich gegen die Richtlinie
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;
Der Gesamtlevel ist das Maximum über alle Findings, die einzig vertretbare Aggregation: Ein Dokument mit neunundneunzig Archiv-Ergänzungen und einer unklassifizierten Änderung ist eine unklassifizierte Änderung
Darunter: Fingerprints, keine kryptografischen Hashes
Die Vergleichs-Engine, die CompareWith bereitstellt und auf der die Modifikationsanalyse aufbaut, identifiziert Objekte über einen Fingerprint ihres normalisierten Körpers mit einem nicht-kryptografischen 64-Bit-Hash statt SHA-256. Das ist eine abgewogene Entscheidung. Was der strukturelle Vergleich braucht, ist Determinismus: Derselbe Objektkörper muss innerhalb eines Laufs immer denselben Fingerprint erzeugen. Kollisionsresistenz braucht er nicht, denn ein Angreifer, der beide Seiten des Vergleichs kontrolliert, hat bereits auf anderen Wegen gewonnen, und die Kosten eines vollen kryptografischen Hashs über jedes Objekt in einem Dokument mit einer Million Objekten sind ein realer Preis ohne Nutzen
Zwei Normalisierungsregeln wiegen schwerer als die Hash-Wahl. Indirekte Referenzen falten zu einem Platzhalter-Token, statt in den referenzierten Inhalt expandiert zu werden: Expansion würde den Körper eines geteilten Objekts in jeden Referenzierer kopieren, also würde eine kleine Änderung an einem geteilten Font-Deskriptor den Fingerprint jedes Objekts ungültig machen, das ihn erreicht, und der Bericht wäre unlesbar. Und Objektnummern selbst werden vom Fingerprint ausgeschlossen, denn ein Rewrite kann Objekte umnummerieren, ohne etwas Semantisches zu ändern
Das Matching läuft dann in zwei Durchgängen, zuerst ausgerichtet nach Fingerprint, dann wird der Rest nach Objektnummer gepaart, um Änderungen zu identifizieren statt einer Ergänzung plus einer Entfernung. Billige Prüfungen kommen überall zuerst: Eine Differenz der Seitenzahl wird gemeldet, bevor irgendeine Objekttraversierung beginnt
Eine Falle: Selbstvergleich ist nicht garantiert identisch
Der natürliche erste Test für eine Diff-Engine ist, eine Datei mit sich selbst zu vergleichen und zu behaupten, das Ergebnis sei identisch. Diese Behauptung hält hier nicht, und der Grund ist lehrreich. Der öffentliche Ladepfad und der tiefer liegende Dokumentladepfad konfigurieren die Dekodierung nicht identisch, also kann dieselbe Datei, über die zwei Routen geladen, Fingerprints liefern, die bei manchen Objekten abweichen. Die Engine hat nicht Unrecht; die beiden Ladevorgänge haben tatsächlich unterschiedliche Speicherzustände erzeugt
Statt die beiden Pfade zusammenzuzwingen, ist die Vergleichssemantik eng formuliert: Die Analyse vergleicht den aktuellen Dokumentzustand gegen eine frühere Revision und meldet identisch nur, wenn die beiden Fingerprint-Mengen exakt übereinstimmen. Das ist die Frage, die Nutzer tatsächlich stellen, und sie verlangt nicht, dass die beiden Loader austauschbar sind. Wer ein Vergleichsfeature entwirft, für den ist zu definieren, was „dasselbe“ bedeutet, mehr Arbeit als es zu berechnen
Wo man es einsetzt
Zwei Orte. In einem Validierungsbericht, neben der Signaturprüfung, damit ein Prüfer nicht nur sieht, ob die Signatur kryptografisch intakt ist, sondern was danach mit dem Dokument geschah; die Signaturseite behandelt PAdES-Signierung und -Validierung. Und in einem Eingangstor, wo ein von außen ankommendes Dokument gegen die von Ihnen gesendete Kopie geprüft wird, sodass ein zurückkommender Vertrag mit einer hinzugefügten Annotation anders behandelt wird als einer mit einer bearbeiteten Seite
Ein Vorbehalt zum Umfang. Diese Analyse sagt Ihnen, was sich zwischen zwei Revisionen derselben Dokumentlinie änderte. Sie sagt nicht, ob sichtbarer Inhalt in die Irre führt, ob der Appearance-Stream eines Formularfelds zu seinem Wert passt oder ob unter einem Overlay versteckter Text noch im Content Stream vorhanden ist. Das braucht eine eigene Behandlung, und die Seite der Inhaltsentfernung davon behandelt der Artikel zur echten Schwärzung. Die Analyse- und Vergleichseinstiegspunkte sind auf der Produktseite der losLab PDF Developer Library dokumentiert