Bhinish Dhiman

Mengapa menggabungkan PDF terenkripsi bisa menghasilkan halaman kosong, hasil pengukuran

Diterjemahkan dari versi asli berbahasa Inggris. Baca versi asli

Menggabungkan PDF yang dilindungi kata sandi melalui pdf-lib upstream dengan ignoreEncryption menghasilkan file empat halaman yang ketiga halaman terlindunginya kosong, dan hanya 49 dari 373 karakter yang selamat, karena isi halaman disalin dalam keadaan masih terenkripsi. Penggabungan yang sama melalui @cantoo/pdf-lib dengan kata sandi menghasilkan empat halaman, 373 karakter, dan tidak ada yang kosong.

Gejalanya sudah banyak dikenal: Anda menggabungkan rekening koran dengan surat pengantar, tidak ada pesan galat yang muncul, dan halaman-halaman rekening koran itu keluar dalam keadaan putih polos. Kami mereproduksinya dalam kode dan menghitung apa saja yang hilang.

Apa yang sebenarnya dienkripsi oleh enkripsi PDF

Enkripsi PDF menerapkan sebuah cipher, yaitu AES-256 menurut ISO 32000-2, pada string dan stream di dalam file, dan hanya pada keduanya. Struktur objeknya tetap terbuka: pembaca PDF bisa melihat bahwa ada tiga halaman, berapa ukurannya, dan content stream mana yang dipakai masing-masing, tanpa kata sandi. Yang tidak bisa dilakukannya tanpa kata sandi adalah mengubah content stream itu dari ciphertext menjadi instruksi menggambar.

Pemisahan itulah seluruh mekanisme di balik kegagalan halaman kosong. Sebuah library bisa menelusuri strukturnya, menyalin halaman-halamannya, dan tidak pernah menyadari bahwa byte yang disalinnya masih terenkripsi.

Pengukurannya

Kami membuat rekening koran tiga halaman berisi 324 karakter teks yang sudah diketahui dan mengenkripsinya dengan AES-256 serta user password, memakai pemanggilan encrypt() yang sama dengan alat Protect PDF kami. Kemudian kami menggabungkannya setelah surat pengantar satu halaman berisi 49 karakter melalui dua jalur: pdf-lib upstream 1.17.1 yang dimuat dengan ignoreEncryption: true, dan @cantoo/pdf-lib 2.11.1 yang dimuat dengan kata sandi, persis seperti yang dilakukan src/lib/pdf/ops.ts. Setiap hasil dibuka di pdf.js 6.3.289, teksnya diekstrak, dan setiap halaman dirender pada setengah skala lalu dipindai untuk mencari piksel yang tidak putih. Dijalankan di Node 24 pada 1 Oktober 2026.

Jalur Halaman yang keluar Karakter yang diekstrak Halaman kosong Kamus Encrypt di output
pdf-lib upstream, ignoreEncryption: true 4 49 3 Tidak
@cantoo/pdf-lib, kata sandi diberikan 4 373 0 Tidak

Kedua jalur melaporkan keberhasilan. Keduanya menghasilkan file yang bisa dibuka tanpa kata sandi. Salah satunya telah membuang rekening koran itu.

Mengapa halamannya kosong, bukan kacau

Secara default, pdf-lib upstream menolak file terenkripsi, dengan pesan yang menyebutkan jalan pintasnya: “Input document to PDFDocument.load is encrypted. You can use PDFDocument.load(..., { ignoreEncryption: true }).” Alat yang ingin terlihat mendukung input terenkripsi mengambil jalan pintas itu.

Dengan flag itu aktif, library mengurai struktur yang bisa dibacanya dan menyalin tiga objek halaman, masing-masing menunjuk ke content stream berisi ciphertext AES. Dokumen baru tidak memiliki kamus enkripsi, jadi pembaca PDF memperlakukan stream itu sebagai data biasa yang dikompresi Flate, mengekstraknya, mendapat derau, lalu berhenti menggambar. pdf.js mencatat Trying to parse invalid object dan Invalid object ref: 14 0 R lalu lanjut. Ke-49 karakter yang selamat adalah isi surat pengantar, yang memang tidak pernah dienkripsi.

Varian di tempat lebih parah

Kami juga memuat file terenkripsi itu dengan ignoreEncryption, mengubah satu kolom metadata, lalu menyimpannya tanpa menyalin apa pun. Output tersebut sama sekali tidak bisa dibuka: pdf.js melaporkan Incorrect Password padahal diberi kata sandi yang benar. Proses penyimpanan telah menulis ulang struktur file di sekitar ciphertext yang kuncinya tidak lagi cocok, sehingga hasilnya adalah dokumen yang tidak bisa dipulihkan siapa pun, bukan sekadar dokumen dengan halaman kosong.

Fork inilah yang melakukan dekripsi

@cantoo/pdf-lib adalah fork dari pdf-lib yang menambahkan opsi password pada load() dan metode encrypt(). Jika diberi kata sandi, fork ini mendekripsi setiap stream saat mengurai, sehingga copyPages memindahkan plaintext, dan output gabungannya memuat seluruh 373 karakter dengan setiap halaman ter-render. Tanpa kata sandi, fork ini melempar galat persis seperti versi upstream: tidak menebak, dan tidak ada jalur untuk menghapus kata sandi yang tidak Anda ketahui.

Pemeriksaan untuk alat penggabung apa pun

Gabungkan file yang dilindungi, lalu ekstrak teksnya, dengan alat PDF to text kami atau alat lain. Jika karakter dari dokumen yang dilindungi tidak ada, alat tersebut menyalin ciphertext. Halaman kosong bukan gangguan render yang bisa diatasi dengan penampil lain; isinya memang tidak selamat dalam proses penggabungan.