Aktualizacje przyrostowe i dlaczego są ważne
ISO 32000-2 §7.5.6 Evidence: Standard-backed
W skrócie
Dział zatytułowany „W skrócie”Gdy plik PDF zmienia się po zapisaniu, bezpieczny sposób zapisu nie polega na przepisaniu całego pliku. Zamiast tego dołącza się zmienione obiekty i nową sekcję odsyłaczy na końcu, pozostawiając każdy oryginalny bajt dokładnie tam, gdzie był. Ta strona wyjaśnia, jak działa ten mechanizm i dlaczego dzięki niemu podpis cyfrowy może przetrwać późniejszą edycję.
Dlaczego to ma znaczenie
Dział zatytułowany „Dlaczego to ma znaczenie”Podpis chroni zakres bajtów. Gdyby zapisanie jednowyrazowej zmiany przepisywało plik, przesunąłby się każdy offset bajtowy. Podpisany zakres nie odnosiłby się już do tej samej treści. Podpis przestałby być prawidłowy, mimo że sama podpisana treść pozostałaby nietknięta.
Aktualizacje przyrostowe istnieją właśnie po to, aby tak się nie działo. Oryginalne bajty, w tym bajty objęte podpisem, pozostają na miejscu. Weryfikator może sprawdzić pierwszy podpis w dokumencie, który po podpisaniu został edytowany, względem oryginalnej wersji. Widzi dokładnie to, co zostało podpisane, oraz – osobno – to, co zmieniło się później. Pomyłka w tym miejscu oznacza albo unieważnienie prawidłowych podpisów, albo – co gorsza – utratę możliwości udowodnienia, co faktycznie poświadczał dany podpis.
Wersja skrócona
Dział zatytułowany „Wersja skrócona”- Aktualizacja przyrostowa dołącza nowe i zmienione obiekty, następnie nową sekcję odsyłaczy, a potem nowy zwiastun (trailer) – wszystko na końcu pliku.
- Oryginalna zawartość pliku pozostaje nienaruszona – nie jest edytowana w miejscu.
- Nowy zwiastun zawiera wpis
/Prev: offset bajtowy poprzedniej sekcji odsyłaczy. Sekcje tworzą łańcuch wsteczny. - Czytnik buduje swój indeks, przechodząc ten łańcuch od najnowszej sekcji. Dla każdego numeru obiektu wygrywa najnowszy wpis.
- Ponieważ nic nie zostało nadpisane, zakres bajtów objęty wcześniejszym podpisem nadal jest identyczny bajt po bajcie – dzięki temu podpis wciąż przechodzi weryfikację, a dokument można odtworzyć dokładnie w postaci, w jakiej został podpisany.
Jak podchodzi do tego NextPDF
Dział zatytułowany „Jak podchodzi do tego NextPDF”NextPDF zapisuje dokument bazowy w sposób opisany na poprzedniej stronie, a następnie udostępnia trzy elementy potrzebne do przygotowania aktualizacji przyrostowej.
Po wywołaniu build() moduł zapisujący (src/Writer/PdfWriter.php) zachowuje:
- bufor wyjściowy, udostępniany przez
getBuffer(), aby aktualizację można było dołączyć dokładnie na końcu istniejących bajtów; - offset bajtowy ostatniej sekcji odsyłaczy, udostępniany przez
getLastXrefOffset(), który staje się wartością/Prevnowej sekcji; - wpisy słownika katalogu, udostępniane przez
getCatalogEntries(), aby aktualizacja, która musi ponownie wyemitować katalog (na przykład w celu dołączenia odwołania do podpisu), nie utraciła żadnego z wcześniejszych kluczy.
Dołączana wersja przydziela nowe numery obiektów (lub ponownie używa istniejących dla obiektów, które zastępuje) w ramach tego samego ObjectRegistry, dzięki czemu numeracja obiektów pozostaje spójna między wersjami. Nowa sekcja odsyłaczy wymienia tylko obiekty, których dotyczy ta wersja. Nowy zwiastun powtarza wpisy poprzedniego zwiastuna i dodaje /Prev, wskazujący wstecz na wcześniejszą sekcję. To właśnie ten łańcuch przechodzi czytnik.
Najlepiej widać to przy podpisywaniu. Klasa NextPDF ByteRangeCalculator (src/Security/Signature/ByteRangeCalculator.php) oblicza tablicę /ByteRange jako dwa segmenty: wszystko przed wartością podpisu oraz wszystko po niej – dzięki czemu podpis obejmuje całą wersję z wyjątkiem własnych bajtów. Ponieważ późniejsza edycja jest dołączana, a nie zapisywana na tych bajtach, ten zakres nigdy się nie przesuwa.
- Write base revision Header, body, xref section, trailer — the original bytes.
- Sign A /ByteRange digest covers the whole revision except the signature value itself.
- Edit and save Changed objects + a new xref section are appended; originals are untouched.
- New trailer chains back The appended trailer carries /Prev = offset of the previous xref section.
- Verify The first signature still covers the same unchanged bytes; the chain shows what came after.
Co mówią dowody
Dział zatytułowany „Co mówią dowody”Zasada wyłącznego dołączania ma charakter normatywny. Spec: ISO 32000-2, §7.5.6 ISO 32000-2 §7.5.6 stanowi, że zawartość pliku PDF można aktualizować przyrostowo bez przepisywania całego pliku oraz że w ramach takiej operacji zmiany muszą być dołączane na końcu pliku, pozostawiając oryginalną zawartość nienaruszoną. Evidence: Standard-backed
Ta sama klauzula definiuje mechanizm działania. Sekcja odsyłaczy dla aktualizacji przyrostowej zawiera wpisy wyłącznie dla obiektów, które zostały zmienione, zastąpione lub usunięte. Usunięte obiekty pozostają w pliku, ale są oznaczone jako usunięte przez odpowiadające im wpisy odsyłaczy. Dodany zwiastun musi zawierać wpis /Prev podający położenie poprzedniej sekcji odsyłaczy. Wpis aktualizacji dla zmienionego obiektu zawiera offset bajtowy nowej kopii zamiast starego offsetu. Czytnik buduje swoje informacje o odsyłaczach tak, aby udostępnić najnowszą kopię każdego obiektu.
Konsekwencję dla podpisu wskazuje wprost
Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 : skrót zakresu bajtów
jest obliczany dla zakresu pliku – zwykle całego pliku, z wyłączeniem
wartości podpisu (wpisu /Contents). Następnie norma wskazuje, że
jeśli podpisany dokument zostaje zmodyfikowany i zapisany z użyciem aktualizacji przyrostowej, dane
odpowiadające zakresowi bajtów oryginalnego podpisu zostają zachowane, więc jeśli
podpis jest prawidłowy, stan dokumentu z chwili podpisania można
odtworzyć. Wyłączne dołączanie nie jest udogodnieniem dodatkowym. To właśnie na tej własności opiera się model
podpisu.
Praktyczny przykład
Dział zatytułowany „Praktyczny przykład”Plik PDF najpierw podpisany, a następnie edytowany, wygląda strukturalnie tak. Oryginalna wersja kończy się własnym znacznikiem %%EOF. Druga wersja jest dołączana poniżej.
%PDF-2.0... original objects, including the signature dictionary ...xref0 8... entries for the original revision ...trailer<< /Size 8 /Root 1 0 R >>startxref920%%EOF <-- end of revision 1: the signed bytes stop here9 0 obj <-- revision 2, appended<< /Type /Annot /Subtype /Text /Contents (added after signing) >>endobjxref0 19 0 obj-entry...8 90000001740 00000 ntrailer<< /Size 10 /Root 1 0 R /Prev 920 >>startxref1980%%EOFWalidator czyta ostatni zwiastun, widzi /Prev 920 i ma dostęp do całego łańcucha. Może zweryfikować podpis względem niezmienionych bajtów aż do pierwszego %%EOF. Może następnie osobno zgłosić, że wersja 2 dodała adnotację. Historia pozostaje w pliku. Nic nie zostało ukryte przez nadpisanie.
Częste nieporozumienie
Dział zatytułowany „Częste nieporozumienie”Pułapką jest przekonanie, że „aktualizacja przyrostowa” oznacza zmianę niewielką, a więc nieszkodliwą. W dołączaniu chodzi o zachowanie bajtów, a nie o rozmiar. Aktualizacja przyrostowa może dodać bardzo dużo treści. O jej przyrostowym charakterze decyduje to, że nie narusza bajtów, które już tam były. Zaskakujący bywa też wniosek: narzędzie, które „optymalizuje” lub „linearyzuje” podpisany plik PDF, przepisując go od nowa, wytworzy mniejszy, czystszy plik oraz uszkodzony podpis, ponieważ podpisany zakres bajtów już nie istnieje. Zapis przyrostowy podpisanego pliku PDF i zapisanie go od nowa to nie ta sama operacja.
Ograniczenia i granice
Dział zatytułowany „Ograniczenia i granice”Wyłączne dołączanie chroni bajty. Samo w sobie nie mówi, czy dołączone zmiany były autoryzowane. Druga wersja może w sposób uprawniony dodać drugi podpis albo może dodać treść, której pierwszy podpisujący nigdy nie zamierzał. Rozstrzygnięcie, która sytuacja zachodzi, jest zadaniem walidacji podpisu i polityki wykrywania modyfikacji (DocMDP). Dołączanie jest podstawą, która czyni tę analizę możliwą, a nie samą analizą.
Ta strona nie omawia również, w jaki sposób obliczane i łączone są dwa zakresy bajtów podpisu ani co sprawdza pełna walidacja. To odrębne tematy. Gwarancja dotyczy tu plików zapisanych i zaktualizowanych przez zgodny ze specyfikacją moduł zapisujący: plik, którego wcześniejsze wersje były już zniekształcone, nie staje się poprawny tylko dlatego, że coś do niego dołączono.
Mini-FAQ
Dział zatytułowany „Mini-FAQ”Skąd wiadomo, ile wersji ma plik PDF? Policz znaczniki %%EOF i prześledź łańcuch /Prev od ostatniego zwiastuna. Każda osiągnięta sekcja odsyłaczy to jedna zapisana wersja.
Czy usunięcie obiektu usuwa go z pliku? Nie. Aktualizacja przyrostowa oznacza obiekt jako usunięty w jego wpisie odsyłaczy, ale bajty obiektu pozostają we wcześniejszych wersjach. „Usunięty” oznacza „nieodwoływany przez bieżącą wersję”, a nie „wymazany”.
Czy aktualizacja przyrostowa może zmienić wersję PDF? Tak, przez ustawienie wpisu /Version w katalogu w dołączonej wersji. Nagłówek pozostaje w postaci, w jakiej został zapisany. Wpis /Version w katalogu ma pierwszeństwo, jeśli wskazuje późniejszą wersję.
Powiązane dokumenty
Dział zatytułowany „Powiązane dokumenty”- Czym właściwie jest plik PDF – model obiektowy i pojedyncza sekcja odsyłaczy, którą rozszerza aktualizacja.
- Jak podpisy osadzają się w pliku PDF – mechanizm zakresu bajtów, który mają chronić aktualizacje przyrostowe.
- Jak poprawnie zweryfikować podpis – co sprawdza prawidłowa walidacja w pełnej historii zmian pliku.
Słowniczek
Dział zatytułowany „Słowniczek”- Aktualizacja przyrostowa – zapisanie zmiany przez dołączenie zmienionych obiektów, nowej sekcji odsyłaczy i nowego zwiastuna na końcu pliku, bez zmiany istniejących bajtów.
/Prev– wpis zwiastuna (lub strumienia odsyłaczy) przechowujący offset bajtowy poprzedniej sekcji odsyłaczy. Łączy wersje w łańcuch wsteczny.- Wersja – stan pliku opisany przez jedną sekcję odsyłaczy i jej zwiastun. Plik z N sekcjami odsyłaczy ma N wersji.
/ByteRange– tablica w słowniku podpisu podająca dwa segmenty bajtów objęte skrótem podpisu (wszystko z wyjątkiem samej wartości podpisu).- Podpisany zakres bajtów – dokładne bajty, dla których obliczono skrót podpisu. Aktualizacje przyrostowe istnieją po to, aby te bajty nigdy nie były przenoszone ani nadpisywane.