Lewati ke konten

Pembaruan inkremental dan alasan pentingnya

Evidence: Standard-backed

Saat sebuah PDF berubah setelah ditulis, cara aman untuk menyimpannya bukan dengan menulis ulang berkas. Sebagai gantinya, Anda menambahkan objek yang berubah dan satu bagian cross-reference baru di bagian akhir, sehingga setiap byte asli tetap berada persis di tempatnya. Halaman ini menjelaskan cara kerjanya dan mengapa mekanisme ini membuat tanda tangan digital tetap bertahan terhadap penyuntingan di kemudian hari.

Sebuah tanda tangan melindungi suatu rentang byte. Jika penyimpanan perubahan satu kata saja menulis ulang berkas, setiap offset byte akan bergeser. Rentang yang ditandatangani tidak lagi menggambarkan konten yang sama. Tanda tangan akan rusak, meskipun konten yang ditandatangani itu sendiri tidak tersentuh.

Pembaruan inkremental ada agar hal itu tidak terjadi. Byte asli, termasuk byte yang dicakup oleh sebuah tanda tangan, tetap di tempatnya. Seorang peninjau dapat mengambil dokumen yang sudah ditandatangani lalu disunting dan memverifikasi tanda tangan pertama terhadap revisi aslinya. Peninjau melihat secara tepat apa yang ditandatangani dan, secara terpisah, apa yang berubah setelahnya. Jika hal ini ditangani keliru, Anda akan membuat tanda tangan yang sah menjadi tidak valid atau, lebih buruk lagi, kehilangan kemampuan untuk membuktikan apa yang sebenarnya disahkan oleh sebuah tanda tangan.

  • Pembaruan inkremental menambahkan: objek baru dan yang berubah, lalu satu bagian cross-reference baru, lalu satu trailer baru, semuanya di bagian akhir berkas.
  • Konten berkas asli dibiarkan utuh — tidak disunting di tempat.
  • Trailer baru membawa entri /Prev: offset byte dari bagian cross-reference sebelumnya. Bagian-bagian ini membentuk rantai mundur.
  • Pembaca membangun indeksnya dengan menelusuri rantai itu mulai dari yang terbaru. Untuk setiap nomor objek, entri paling baru yang berlaku.
  • Karena tidak ada yang ditimpa, rentang byte yang dicakup oleh tanda tangan sebelumnya tetap sama persis byte demi byte — sehingga tanda tangan tetap terverifikasi, dan Anda dapat memulihkan dokumen persis seperti saat ditandatangani.

NextPDF menulis dokumen dasar seperti yang dijelaskan di halaman sebelumnya, lalu menyediakan tiga hal yang dibutuhkan pembaruan inkremental.

Setelah build(), writer (src/Writer/PdfWriter.php) menyimpan:

  • buffer keluaran, yang dapat diambil melalui getBuffer(), sehingga pembaruan dapat ditambahkan tepat di akhir byte yang sudah ada;
  • offset byte dari bagian cross-reference terakhir, melalui getLastXrefOffset(), sebagai nilai /Prev untuk bagian yang baru;
  • entri dictionary katalog, melalui getCatalogEntries(), sehingga pembaruan yang harus menulis ulang katalog (misalnya untuk melampirkan referensi tanda tangan) tidak kehilangan kunci-kunci yang sudah ada.

Revisi yang ditambahkan mengalokasikan nomor objek baru (atau menggunakan kembali nomor yang ada untuk objek yang digantikannya) dengan ObjectRegistry yang sama, sehingga penomoran objek tetap konsisten di seluruh revisi. Bagian cross-reference baru hanya mencantumkan objek yang disentuh oleh revisi ini. Trailer baru mengulang entri-entri trailer sebelumnya dan menambahkan /Prev, yang menunjuk kembali ke bagian sebelumnya. Rantai itulah yang diikuti oleh pembaca.

Tempat paling jelas peran mekanisme ini adalah penandatanganan. ByteRangeCalculator milik NextPDF (src/Security/Signature/ByteRangeCalculator.php) menghitung array /ByteRange sebagai dua segmen: segala sesuatu sebelum nilai tanda tangan, dan segala sesuatu setelahnya — sehingga tanda tangan mencakup seluruh revisi kecuali byte-nya sendiri. Karena penyuntingan berikutnya ditambahkan, bukan ditulis dengan menimpa byte-byte tersebut, rentang itu tidak pernah bergeser.

  1. Write base revision Header, body, xref section, trailer — the original bytes.
  2. Sign A /ByteRange digest covers the whole revision except the signature value itself.
  3. Edit and save Changed objects + a new xref section are appended; originals are untouched.
  4. New trailer chains back The appended trailer carries /Prev = offset of the previous xref section.
  5. Verify The first signature still covers the same unchanged bytes; the chain shows what came after.
Bagaimana PDF yang ditandatangani lalu disunting tetap dapat diverifikasi: setiap penyimpanan menambahkan data, sehingga byte yang ditandatangani semula tidak pernah ditimpa dan rantai trailer merekam riwayatnya.

Aturan append-only bersifat normatif. Spec: ISO 32000-2, §7.5.6 menyatakan bahwa konten sebuah PDF dapat diperbarui secara inkremental tanpa menulis ulang seluruh berkas, dan ketika itu dilakukan, perubahan harus ditambahkan ke bagian akhir berkas sambil membiarkan konten asli tetap utuh. Evidence: Standard-backed

Klausul yang sama mendefinisikan mekanismenya. Bagian cross-reference untuk sebuah pembaruan inkremental hanya berisi entri untuk objek yang diubah, diganti, atau dihapus. Objek yang dihapus tetap berada di dalam berkas tetapi ditandai terhapus melalui entri cross-reference-nya. Trailer yang ditambahkan harus berisi entri /Prev yang memberikan lokasi bagian cross-reference sebelumnya. Entri pembaruan untuk objek yang berubah membawa offset byte dari salinan baru, menggantikan offset yang lama. Pembaca menyusun informasi cross-reference-nya sehingga salinan paling baru dari setiap objeklah yang diakses.

Konsekuensi terhadap tanda tangan dinyatakan secara langsung oleh Spec: ISO 32000-2, §12.8.1 : sebuah digest rentang byte dihitung atas suatu rentang berkas — biasanya seluruh berkas, kecuali nilai tanda tangan (entri /Contents). Standar tersebut kemudian mencatat bahwa jika sebuah dokumen yang ditandatangani dimodifikasi dan disimpan melalui pembaruan inkremental, data yang berkaitan dengan rentang byte tanda tangan asli tetap dipertahankan, sehingga bila tanda tangan valid, keadaan dokumen pada saat penandatanganan dapat diciptakan kembali. Append-only bukan sekadar pelengkap. Inilah properti yang menjadi sandaran model tanda tangan.

Sebuah PDF yang sudah ditandatangani lalu disunting, jika dilihat secara struktural. Revisi asli berakhir pada %%EOF-nya sendiri. Revisi kedua ditambahkan di bawahnya.

%PDF-2.0
... original objects, including the signature dictionary ...
xref
0 8
... entries for the original revision ...
trailer
<< /Size 8 /Root 1 0 R >>
startxref
920
%%EOF
<-- end of revision 1: the signed bytes stop here
9 0 obj <-- revision 2, appended
<< /Type /Annot /Subtype /Text /Contents (added after signing) >>
endobj
xref
0 1
9 0 obj-entry...
8 9
0000001740 00000 n
trailer
<< /Size 10 /Root 1 0 R /Prev 920 >>
startxref
1980
%%EOF

Validator membaca trailer terakhir, melihat /Prev 920, dan kini memperoleh seluruh rantainya. Ia dapat memverifikasi tanda tangan terhadap byte hingga %%EOF pertama, yang tidak berubah. Ia kemudian dapat melaporkan secara terpisah bahwa revisi 2 menambahkan sebuah anotasi. Riwayatnya ada di dalam berkas. Tidak ada yang disembunyikan melalui penimpaan.

Jebakan umumnya adalah anggapan “pembaruan inkremental berarti perubahannya kecil, jadi tidak berbahaya.” Penambahan data berkaitan dengan pelestarian byte, bukan ukuran. Sebuah pembaruan inkremental dapat menambahkan banyak sekali konten. Yang menjadikannya pembaruan inkremental adalah bahwa ia tidak menyentuh byte yang sudah ada di sana. Konsekuensinya juga sering menjebak: alat yang “mengoptimalkan” atau “melinearisasi” PDF yang ditandatangani dengan menulis ulang dari awal akan menghasilkan berkas yang lebih kecil dan lebih rapi serta tanda tangan yang rusak, karena rentang byte yang ditandatangani tidak lagi ada. Menyimpan PDF yang ditandatangani dan menyimpannya kembali bukanlah operasi yang sama.

Append-only melindungi byte. Dengan sendirinya, ia tidak memberi tahu Anda apakah perubahan yang ditambahkan itu terotorisasi. Revisi kedua dapat secara sah menambahkan tanda tangan kedua, atau dapat menambahkan konten yang tidak pernah dikehendaki oleh penanda tangan pertama. Menentukan skenario mana yang terjadi adalah tugas validasi tanda tangan dan kebijakan deteksi modifikasi (DocMDP). Penambahan data adalah fondasi yang membuat analisis itu mungkin, bukan analisis itu sendiri.

Halaman ini juga tidak membahas bagaimana dua rentang byte sebuah tanda tangan dihitung dan disambung, ataupun apa yang diperiksa oleh validasi yang lengkap. Itu adalah topik-topik tersendiri. Jaminan di sini berlaku untuk berkas yang ditulis dan diperbarui oleh writer yang patuh: sebuah berkas yang revisi-revisi sebelumnya sudah cacat tidak menjadi sahih hanya karena ditambahi data.

Bagaimana cara saya mengetahui berapa banyak revisi yang dimiliki sebuah PDF? Hitung penanda %%EOF dan ikuti rantai /Prev dari trailer terakhir. Setiap bagian cross-reference yang ditemukan adalah satu revisi yang tersimpan.

Apakah menghapus sebuah objek menghilangkannya dari berkas? Tidak. Sebuah pembaruan inkremental menandai objek tersebut terhapus dalam entri cross-reference-nya, tetapi byte objek itu tetap ada di revisi-revisi sebelumnya. “Terhapus” berarti “tidak direferensikan oleh revisi saat ini,” bukan “terhapus permanen.”

Bisakah sebuah pembaruan inkremental mengubah versi PDF? Ya, dengan mengatur entri /Version dalam katalog pada revisi yang ditambahkan. Header tetap seperti yang tertulis. /Version dalam katalog lebih diutamakan ketika ia menyebutkan versi yang lebih baru.

  • Pembaruan inkremental — menyimpan perubahan dengan menambahkan objek yang berubah, satu bagian cross-reference baru, dan satu trailer baru ke bagian akhir berkas, tanpa mengubah byte yang sudah ada.
  • /Prev — entri trailer (atau cross-reference stream) yang menyimpan offset byte dari bagian cross-reference sebelumnya. Entri ini menghubungkan revisi-revisi menjadi satu rantai mundur.
  • Revisi — keadaan berkas yang ditangkap oleh satu bagian cross-reference dan trailer-nya. Sebuah berkas dengan N bagian cross-reference memiliki N revisi.
  • /ByteRange — array dalam dictionary tanda tangan yang menyebutkan dua segmen byte yang dicakup oleh digest tanda tangan (segala sesuatu kecuali nilai tanda tangan itu sendiri).
  • Rentang byte yang ditandatangani — byte persis yang menjadi dasar penghitungan digest sebuah tanda tangan. Pembaruan inkremental ada agar byte-byte ini tidak pernah dipindahkan atau ditimpa.