Sebuah flag izin bukanlah mekanisme keamanan. Bit yang berbunyi "dilarang menyalin" tinggal di dalam kamus /Encrypt yang sama dengan kriptografinya, dan itu memberinya kesan penegakan yang sebenarnya tidak ia miliki, dan begitu Anda memperlakukan keduanya sebagai satu hal, audit Anda mulai menghasilkan jawaban yang salah. Satu-satunya pertanyaan yang layak diajukan pada sebuah PDF bukanlah "apakah ia terenkripsi." Pertanyaannya lebih spesifik dan lebih sulit: algoritma apa, revisi security handler yang mana, kata sandi mana dari dua yang ditetapkan, bit izin apa saja yang diklaim, dan bagian berkas mana yang sungguh disentuh enkripsi itu. Sebuah berkas bisa terenkripsi secara formal namun terbuka secara praktis. Ia bisa menolak dibaca tetapi meninggalkan metadatanya dalam teks polos. Ia bisa mengunci pencetakan lewat flag yang bebas diabaikan viewer mana pun. Mengaudit PDF berarti menuntaskan semua itu secara terpisah, dan PDF Library for Delphi, mesin PDF milik losLab untuk Delphi dan C++Builder, memaparkan masing-masingnya lewat API datar berbasis handle integer sekaligus lapisan kelas bertipe
Apa yang sebenarnya dicatat kamus /Encrypt
ISO 32000-1 §7.6 mendefinisikan keamanan dokumen melalui segelintir entri kamus, dan PDF Library for Delphi mencerminkannya satu lawan satu dalam record TPDFEncryption. Versi filter V dan revisi R memilih keluarga algoritma. Length membawa ukuran kunci. Bit izin duduk di P, string validasi kata sandi owner dan user ada di O dan U (dengan OE dan UE ditambahkan untuk AES-256), sebuah flag EncryptMetadata menyertainya, dan tiga field lagi menyebut crypt filter yang diterapkan berturut-turut pada string, stream, dan berkas tertanam
Nilai dari record ini adalah bahwa ia tidak menafsirkan apa pun untuk Anda. Ia menyerahkan kamus mentahnya dan membiarkan Anda menarik kesimpulan, dan itu persis yang dibutuhkan sebuah audit. Kasus teks polos di dalam berkas terenkripsi tampak pada StringFilterIdentity dan StreamFilterIdentity: ketika salah satunya bernilai true, data yang bersangkutan melewati filter Identity tanpa tersentuh, tidak peduli apa yang dilaporkan status terenkripsi dokumen itu. Pemindai yang berhenti pada "ada kamus /Encrypt" akan menyebut berkas semacam itu terlindungi padahal string dan stream-nya terhampar terbuka. Nuansa yang sama mengatur metadata. Ketika EncryptMetadata bernilai false, paket XMP tetap terbaca oleh pengindeks mana pun sementara konten halamannya tidak, dan itu penting diketahui begitu aturan perutean Anda bergantung pada field judul atau penulis
Probe keamanan singkat dengan API datar
Untuk kebanyakan pipeline, empat pemanggilan datar menjawab pertanyaan sehari-hari. LoadFromFile mengembalikan 1 bila berhasil, dan begitu dokumen terbuka, para inspektor enkripsi melaporkan keadaan terdekripsinya:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
raise Exception.Create('Open failed: wrong password or damaged file');
Writeln('status : ', PDF.EncryptionStatus); // terdekripsi / terenkripsi / tak diketahui
Writeln('algorithm : ', PDF.EncryptionAlgorithm); // keluarga RC4 vs AES
Writeln('strength : ', PDF.EncryptionStrength); // kelas panjang kunci
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword lebih penting daripada yang disiratkan signature satu barisnya. PDF mendefinisikan dua kata sandi dengan kuasa yang tidak setara. Kata sandi user diperlukan sekadar untuk membuka berkas. Kata sandi owner memberi hak penuh dan mengatasi setiap bit izin. Byte di disk sama saja pada kedua kasus, tetapi sesi yang dibuka dengan kata sandi owner dapat melakukan hal-hal yang tidak bisa dilakukan sesi kata sandi user, sehingga audit yang tidak mencatat kredensial mana yang disodorkan hanya mencatat separuh kebenaran. Lapisan kelas membuat perbedaan itu dapat dikueri. TPDFDocument.HasUserPassword dan HasOwnerPassword melaporkan apa yang diminta berkas, sementara IsUserPassword dan IsOwnerPassword melaporkan kata sandi mana yang sesungguhnya membuka sesi saat ini. Catat fakta itu. Jangan pernah mencatat nilai kata sandinya
Tangga Strength, tempat "AES-256" berarti dua hal
Fungsi datar Encrypt dan EncryptFile menerima Strength bertipe integer dengan lima nilai bermakna: 0 untuk RC4 40-bit, 1 untuk RC4 128-bit, 2 untuk AES 128-bit yang terbaca sejak Acrobat 7, 3 untuk AES 256-bit sebagaimana diperkenalkan bersama Acrobat 9, dan 4 untuk AES 256-bit sebagaimana disyaratkan Acrobat X ke atas
Bagian menariknya, 3 dan 4 sama-sama berlabel AES-256 padahal bukan skema yang sama. Strength 3 memetakan ke revisi security handler 5, sebuah rancangan sementara yang dikirim Acrobat 9 dan tidak pernah diadopsi ISO. Strength 4 memetakan ke revisi 6, yang fungsi turunan kuncinya diperkuat dan dibakukan dalam ISO 32000-2. Untuk dokumen yang Anda buat hari ini, tidak ada alasan memilih 3 ketimbang 4. Untuk sebuah audit, jurang itu menentukan: kebijakan yang berbunyi "AES-256 sesuai ISO 32000-2" hanya dipenuhi oleh R6, dan berkas R5 yang menyebut dirinya AES-256 gagal memenuhi kebijakan itu sambil lolos pemeriksaan kekuatan yang naif. Lapisan kelas memisahkan keduanya lewat nama, esAES256Bit untuk R5 berhadapan dengan esAES256BitAcroX untuk R6, dan properti EncryptionAcroX menjawab pertanyaan revisi itu dengan satu boolean
Bit izin dan cetakan kecil soal panjang kuncinya
EncodePermissions memampatkan delapan flag menjadi integer yang diharapkan Encrypt dan EncryptFile. Print, copy, change, dan add-notes membentuk himpunan dasar; fill-fields, copy-for-accessibility, assemble, dan full-quality print membentuk himpunan lanjutan. Cetakan kecilnya, yang dinyatakan terang-terangan oleh demo enkripsi milik pustaka ini, adalah bahwa empat flag lanjutan itu baru berlaku pada kekuatan 128-bit ke atas. Flag full-quality print tunduk pada aturan yang sama: matikan ia untuk memaksa pencetakan resolusi rendah, dan dokumen 40-bit akan mengabaikan Anda, karena penurunan itu pun memerlukan enkripsi 128-bit atau lebih kuat. Kodekan kebijakan "hanya cetak resolusi rendah" ke dalam berkas 40-bit dan setiap viewer tetap akan mencetak dengan kualitas penuh
Pertanyaan yang lebih dalam adalah siapa yang menegakkan bit-bit ini, dan jawabannya tidak ada satu pun yang bisa Anda percaya. Izin adalah instruksi bagi pembaca yang patuh, bukan pembatasan kriptografis. Kunci dekripsinya identik entah penyalinan diizinkan atau ditolak, jadi himpunan izin yang terkunci rapat hanya menjaga viewer yang jujur tetap jujur. Pembaca yang memilih mengabaikan bit itu sama sekali tidak menghadapi rintangan kriptografis. Jika kewajibannya adalah mencegah ekstraksi dan bukan sekadar mengurangi minat, berkasnya perlu kata sandi user dan alur kerjanya perlu kontrol di tingkat proses di sekelilingnya, dan laporan audit sebaiknya menyebut rezim mana dari keduanya yang sungguh berlaku pada tiap berkas alih-alih memperlakukan flag izin sebagai gembok
Menetapkan kebijakan dan membuktikannya melekat
Menerapkan enkripsi pada berkas yang sudah ada tidak menuntut pemuatannya ke dalam pohon objek. EncryptFile memproses masukan menjadi keluaran dalam satu pemanggilan, dan loop audit membuka ulang hasilnya untuk memastikan apa yang mendarat di disk. Demo enkripsi yang disertakan mengikuti bentuk tulis-lalu-baca-ulang yang sama:
var
PDF: TPDFlib;
R: Integer;
begin
PDF := TPDFlib.Create;
try
R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
PDF.EncodePermissions(1, 0, 0, 0, // cetak diizinkan; salin/ubah/catatan ditolak
0, 0, 0, 1)); // himpunan lanjutan: hanya cetak kualitas penuh
if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
begin
Writeln('algorithm = ', PDF.EncryptionAlgorithm);
Writeln('strength = ', PDF.EncryptionStrength);
Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
end;
finally
PDF.Free;
end;
end;
Tim yang bekerja di lapisan dokumen mendapat operasi yang sama dengan set bertipe menggantikan pengemasan bit, dan itu lolos tinjauan kode dengan jauh lebih sedikit memicingkan mata:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
Bagaimanapun caranya, langkah baca-ulang bukan seremoni opsional. Ia menangkap kesalahan penerapan yang jika tidak akan muncul berbulan-bulan kemudian di mesin pelanggan: build pustaka lama yang diam-diam menurunkan kekuatan yang diminta, path keluaran yang tidak pernah tertulis karena direktorinya read-only, integer izin yang argumennya masuk dengan urutan keliru. Ketiganya lolos smoke test lokal dan gagal di lapangan, dan membuka ulang keluarannya mengubah masing-masingnya menjadi exception yang Anda lihat saat eksekusi yang membuat berkas itu. GetEncryptionFingerprint mengembalikan nilai ringkas yang bisa Anda simpan bersama catatan pekerjaan, sehingga perbandingan di kemudian hari dapat memberi tahu apakah dua keluaran berbagi konfigurasi enkripsi yang sama tanpa membuka keduanya lagi
Positif palsu audit yang layak diantisipasi dalam kode
Beberapa pola secara konsisten mendorong pemindai keamanan ke kesimpulan yang keliru, dan masing-masing lahir dari meruntuhkan pertanyaan bertingkat menjadi jawaban ya-atau-tidak. Crypt filter Identity adalah contoh paling gamblang. Kamus /Encrypt hadir, berkas melaporkan diri sebagai terenkripsi, namun string dan stream-nya melewati filter Identity tanpa berubah, sehingga konten sesungguhnya berupa teks polos. Membaca StringFilterIdentity dan StreamFilterIdentity sebelum menyatakan apa pun terlindungi adalah perbaikannya
Pemisahan metadata lebih halus. EncryptMetadata dapat berselisih dengan sisa dokumen ke dua arah, meninggalkan berkas terenkripsi dengan paket XMP yang terbaca atau, lebih jarang, sebaliknya. "Berkas ini terenkripsi" tidak mengatakan apa pun tentang apakah metadatanya juga terenkripsi, dan itu penting begitu pengindeks atau aturan perutean menjangkau judulnya. Berkas tertanam menambah sumbu ketiga: PDF mengizinkan crypt filter khusus hanya untuk lampiran, sehingga lampiran bisa menjadi satu-satunya bagian terenkripsi dari dokumen yang selebihnya terbuka, atau satu-satunya bagian teks polos dari dokumen yang terenkripsi. Rekam ketiga penetapan filter itu sebagai field terpisah untuk string, stream, dan berkas tertanam, dan tidak satu pun jebakan ini bisa menjerat Anda. Simpan satu boolean saja dan keputusan yang salah hanyalah soal waktu
Melepas enkripsi, dan memilihnya untuk berkas baru
Sebuah audit sering berujung pada keputusan untuk melucuti perlindungan, dan mekanismenya bukan rintangan di situ. DecryptFile(InputFileName, OutputFileName, Password) menulis salinan terdekripsi tanpa pemuatan penuh, dan Decrypt pada dokumen yang sudah dimuat melakukan hal serupa di memori begitu berkasnya terbuka. Keduanya menuntut kata sandi yang sah; tidak satu pun menyiasati kriptografinya. Gerbang yang sebenarnya adalah kebijakan, bukan kode, jadi buat aturan penerimaan Anda menyatakan dengan jelas kapan pelepasan diizinkan dan catat kelas kata sandi yang mengesahkannya, sebab langkah teknisnya sendiri tidak meninggalkan jejak
Pilihan untuk keluaran baru lebih sempit daripada yang disiratkan lima nilai Strength itu. Gunakan Strength 4, AES-256 revisi 6, kecuali Anda harus membuka berkas di viewer yang lebih tua dari Acrobat X. Strength 2, AES-128, adalah lantai pragmatis untuk armada viewer menua yang tidak bisa dimutakhirkan. Opsi RC4 di 0 dan 1 ada supaya Anda dapat membaca dan mengaudit arsip lama, bukan supaya Anda memproduksi sesuatu yang baru dengannya; menjangkaunya pada rancangan tahun 2026 adalah tanda bahwa ada persyaratan di hulu yang sudah basi
Keadaan enkripsi mengalir langsung ke keputusan penandatanganan, sebab workbench yang memvalidasi dan menandatangani dokumen membutuhkan disiplin baca-ulang yang sama seperti yang diandalkan audit ini. Wilayah itu dibahas di artikel workbench kepatuhan dan penandatanganan. Ketika sebuah batch menerapkan EncryptFile pada ribuan dokumen besar, panduan direct-access untuk PDF besar menunjukkan cara menjaga penggunaan memori tetap datar selama prosesnya. Referensi API enkripsi selengkapnya tersedia di halaman produk PDF Library for Delphi