Incrementele updates en waarom ze ertoe doen
ISO 32000-2 §7.5.6 Evidence: Standard-backed
In een oogopslag
Sectie met titel “In een oogopslag”Wanneer een PDF wordt gewijzigd nadat hij is geschreven, is de veilige manier om hem op te slaan niet om het bestand te herschrijven. In plaats daarvan voeg je de gewijzigde objecten en een nieuwe cross-reference-sectie aan het einde toe, zodat elke oorspronkelijke byte precies blijft staan waar hij stond. Deze pagina legt uit hoe dat werkt en waarom een digitale handtekening daardoor een latere bewerking kan overleven.
Waarom dit ertoe doet
Sectie met titel “Waarom dit ertoe doet”Een handtekening beschermt een bytebereik. Als bij het opslaan van een wijziging van één woord het bestand zou worden herschreven, zou elke byte-offset verschuiven. Het ondertekende bereik zou niet langer dezelfde inhoud beschrijven. De handtekening zou ongeldig worden, ook al bleef de ondertekende inhoud zelf onaangeroerd.
Incrementele updates zijn er om dit te voorkomen. De oorspronkelijke bytes, inclusief de bytes die een handtekening dekt, blijven op hun plaats. Een validator kan een ondertekend en daarna bewerkt document nemen en de eerste handtekening verifiëren tegen de oorspronkelijke revisie. De validator ziet precies wat er is ondertekend en, los daarvan, wat er daarna is gewijzigd. Als je dit verkeerd doet, maak je ofwel geldige handtekeningen ongeldig of, erger nog, verlies je de mogelijkheid om aan te tonen wat een handtekening daadwerkelijk bevestigde.
De korte versie
Sectie met titel “De korte versie”- Een incrementele update voegt toe: nieuwe en gewijzigde objecten, daarna een nieuwe cross-reference-sectie, daarna een nieuwe trailer, alles aan het einde van het bestand.
- De oorspronkelijke bestandsinhoud blijft intact — niet op zijn oorspronkelijke plek bewerkt.
- De nieuwe trailer bevat een
/Prev-vermelding: de byte-offset van de vorige cross-reference-sectie. De secties vormen een achterwaartse keten. - Een lezer bouwt zijn index op door die keten te doorlopen, te beginnen bij de nieuwste sectie. Voor elk objectnummer wint de meest recente vermelding.
- Omdat er niets is overschreven, is het bytebereik dat een eerdere handtekening dekte nog steeds byte voor byte hetzelfde — dus de handtekening kan nog steeds worden geverifieerd en je kunt het document exact herstellen zoals het was ondertekend.
Hoe NextPDF dit aanpakt
Sectie met titel “Hoe NextPDF dit aanpakt”NextPDF schrijft het basisdocument zoals beschreven op de vorige pagina en stelt vervolgens de drie dingen beschikbaar die een incrementele update nodig heeft.
Na build() bewaart de writer (src/Writer/PdfWriter.php):
- de uitvoerbuffer, opvraagbaar via
getBuffer(), zodat een update precies aan het einde van de bestaande bytes kan worden toegevoegd; - de byte-offset van de laatste cross-reference-sectie, via
getLastXrefOffset(), die de/Prev-waarde van de nieuwe sectie wordt; - de vermeldingen van de catalog-dictionary, via
getCatalogEntries(), zodat een update die de catalog opnieuw moet uitschrijven (bijvoorbeeld om een handtekeningverwijzing toe te voegen) bestaande sleutels niet kwijtraakt.
Een toegevoegde revisie wijst nieuwe objectnummers toe (of hergebruikt bestaande voor objecten die zij vervangt) op basis van dezelfde ObjectRegistry, zodat de objectnummering over revisies heen consistent blijft. De nieuwe cross-reference-sectie vermeldt alleen de objecten die deze revisie heeft aangeraakt. De nieuwe trailer herhaalt de vermeldingen van de vorige trailer en voegt /Prev toe, dat terugwijst naar de voorgaande sectie. Dat is de keten die een lezer volgt.
Dit is het duidelijkst bij ondertekenen. De NextPDF-ByteRangeCalculator (src/Security/Signature/ByteRangeCalculator.php) berekent de /ByteRange-array als twee segmenten: alles vóór de handtekeningwaarde en alles erna — zodat de handtekening de hele revisie dekt behalve haar eigen bytes. Omdat een latere bewerking wordt toegevoegd en niet over die bytes heen wordt geschreven, verschuift dat bereik nooit.
- 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.
Wat het bewijs zegt
Sectie met titel “Wat het bewijs zegt”De append-only-regel is normatief. Spec: ISO 32000-2, §7.5.6 ISO 32000-2 §7.5.6 bepaalt dat de inhoud van een PDF incrementeel kan worden bijgewerkt zonder het hele bestand te herschrijven, en dat wijzigingen daarbij shall worden toegevoegd aan het einde van het bestand, terwijl de oorspronkelijke inhoud intact blijft. Evidence: Standard-backed
Dezelfde clausule beschrijft hoe dit werkt. Een cross-reference-sectie voor een incrementele update bevat alleen vermeldingen voor objecten die zijn gewijzigd, vervangen of verwijderd. Verwijderde objecten blijven in het bestand staan, maar worden via hun cross-reference-vermeldingen als verwijderd gemarkeerd. De toegevoegde trailer shall een /Prev-vermelding bevatten die de locatie van de vorige cross-reference-sectie aangeeft. De updatevermelding voor een gewijzigd object bevat de byte-offset van de nieuwe kopie, die de oude offset vervangt. Een lezer bouwt zijn cross-reference-informatie zo op dat de meest recente kopie van elk object degene is die wordt benaderd.
Het gevolg voor de handtekening wordt rechtstreeks vermeld door
Spec: ISO 32000-2, §12.8.1 ISO 32000-2 §12.8.1 : een byte-range-digest
wordt berekend over een bereik van het bestand — normaal gesproken het hele bestand, met uitzondering van
de handtekeningwaarde (de /Contents-vermelding). De standaard merkt vervolgens op dat
wanneer een ondertekend document wordt gewijzigd en via een incrementele update wordt opgeslagen, de gegevens
die overeenkomen met het bytebereik van de oorspronkelijke handtekening behouden blijven. Als
de handtekening geldig is, kan de toestand van het document op het moment van ondertekenen daardoor worden
gereconstrueerd. Append-only is geen luxe. Het is de eigenschap waarvan het
handtekeningmodel afhangt.
Praktijkvoorbeeld
Sectie met titel “Praktijkvoorbeeld”Een ondertekende-en-vervolgens-bewerkte PDF, structureel bekeken. De oorspronkelijke revisie eindigt bij haar eigen %%EOF. De tweede revisie wordt eronder toegevoegd.
%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%%EOFEen validator leest de laatste trailer, ziet /Prev 920 en heeft daarmee de hele keten. Hij kan de handtekening verifiëren tegen de bytes tot aan de eerste %%EOF, die ongewijzigd zijn. Vervolgens kan hij apart rapporteren dat revisie 2 een annotatie heeft toegevoegd. De geschiedenis zit in het bestand. Niets is verborgen door overschrijven.
Veelvoorkomend misverstand
Sectie met titel “Veelvoorkomend misverstand”De valkuil is “incrementele update betekent dat de wijziging klein is, dus onschadelijk.” Bij toevoegen gaat het om bytebehoud, niet om grootte. Een incrementele update kan zeer veel inhoud toevoegen. Wat het een incrementele update maakt, is dat hij de bytes die er al stonden niet aanraakt. Het gevolg verrast mensen ook: een tool die een ondertekende PDF “optimaliseert” of “lineariseert” door deze vanaf nul te herschrijven, levert een kleiner, schoner bestand op én een ongeldige handtekening, omdat het ondertekende bytebereik niet langer bestaat. Een ondertekende PDF opslaan en opnieuw opslaan zijn niet dezelfde bewerking.
Grenzen en beperkingen
Sectie met titel “Grenzen en beperkingen”Append-only beschermt de bytes. Het zegt op zichzelf niets over de vraag of de toegevoegde wijzigingen geautoriseerd waren. Een tweede revisie kan rechtmatig een tweede handtekening toevoegen, maar kan ook inhoud toevoegen die de eerste ondertekenaar nooit heeft beoogd. Bepalen welke van beide het geval is, is de taak van handtekeningvalidatie en wijzigingsdetectiebeleid (DocMDP). Toevoegen is de onderlaag die die analyse mogelijk maakt, niet de analyse zelf.
Deze pagina behandelt ook niet hoe de twee bytebereiken van een handtekening worden berekend en samengevoegd, of wat een volledige validatie controleert. Dat zijn afzonderlijke onderwerpen. De garantie hier gaat over bestanden die door een conforme writer zijn geschreven en bijgewerkt: een bestand waarvan eerdere revisies al misvormd waren, wordt niet goed gevormd doordat er iets aan wordt toegevoegd.
Mini-FAQ
Sectie met titel “Mini-FAQ”Hoe weet ik hoeveel revisies een PDF heeft? Tel de %%EOF-markeringen en volg de /Prev-keten vanaf de laatste trailer. Elke cross-reference-sectie die je bereikt is één opgeslagen revisie.
Verwijdert het wissen van een object het uit het bestand? Nee. Een incrementele update markeert het object als verwijderd in zijn cross-reference-vermelding, maar de bytes van het object blijven in eerdere revisies staan. “Verwijderd” betekent “niet gerefereerd door de huidige revisie”, niet “gewist.”
Kan een incrementele update de PDF-versie wijzigen? Ja, door de /Version-vermelding in de catalog in de toegevoegde revisie in te stellen. De header blijft zoals geschreven. De /Version in de catalog heeft voorrang wanneer deze een latere versie noemt.
Verwante documentatie
Sectie met titel “Verwante documentatie”- Wat een PDF eigenlijk is — het objectmodel en de enkele cross-reference-sectie waarop een update voortbouwt.
- Hoe handtekeningen in een PDF zitten — het bytebereikmechanisme dat incrementele updates beschermen.
- Een handtekening correct valideren — wat correcte validatie in de revisiegeschiedenis van een bestand controleert.
Verklarende woordenlijst
Sectie met titel “Verklarende woordenlijst”- Incrementele update — een wijziging opslaan door de gewijzigde objecten, een nieuwe cross-reference-sectie en een nieuwe trailer aan het einde van het bestand toe te voegen, zonder bestaande bytes te wijzigen.
/Prev— de vermelding in de trailer (of cross-reference-stream) die de byte-offset van de vorige cross-reference-sectie bevat. Die koppelt revisies tot een achterwaartse keten.- Revisie — de bestandstoestand die wordt vastgelegd door één cross-reference-sectie en de bijbehorende trailer. Een bestand met N cross-reference-secties heeft N revisies.
/ByteRange— de array in een handtekening-dictionary die de twee bytesegmenten aangeeft die de handtekening-digest dekt (alles behalve de handtekeningwaarde zelf).- Ondertekend bytebereik — de exacte bytes waarover de digest van een handtekening is berekend. Incrementele updates zijn er zodat deze bytes nooit worden verplaatst of overschreven.