Lewati ke konten

Mengenkripsi PDF dan membatasi izin

Resep ini mengenkripsi dokumen dengan security handler standar Advanced Encryption Standard (AES)-256. Anda menetapkan kata sandi pengguna (diperlukan untuk membuka), kata sandi pemilik (akses penuh), dan bitmask izin untuk membatasi operasi. Perlakukan izin tersebut sebagai izin yang bergantung pada kerja sama pembaca: enkripsi memberikan kerahasiaan, bukan integritas, dan hanya perangkat lunak yang bekerja sama yang menghormati bit izin. Resep ini mengikuti examples/22-protection.php.

Batas kepercayaan (sertakan ini pada setiap klaim izin). Enkripsi PDF melindungi kerahasiaan konten dari pihak yang tidak memiliki kata sandi (ISO 32000-2 §7.6). Enkripsi tidak melindungi integritas: ia tidak mendeteksi atau mencegah modifikasi. Entri izin P adalah kumpulan flag 32-bit tak bertanda yang meminta pembaca patuh untuk menghormati pembatasan; entri ini bukan kontrol akses. Alat yang tidak patuh, atau alat apa pun yang digunakan dengan kata sandi pemilik, dapat melakukan setiap operasi yang “ditolak”. Jangan menyebut PDF terenkripsi sebagai “aman”, “antirusak”, atau “terlindung dari penyalinan”.

Terminal window
composer require nextpdf/core:^3

Aktifkan ekstensi PHP openssl. Enkriptor AES-256 menggunakan ekstensi tersebut untuk cipher dan derivasi kunci.

Kode V/R dalam kamus enkripsi memilih security handler standar (ISO 32000-2 §7.6). NextPDF Aes256Encryptor mengimplementasikan crypt filter AESV3 pada revisi 6 security handler (V=5/R=6). Ia menggunakan kunci enkripsi berkas 256-bit acak, derivasi kunci hash iteratif ber-salt (Algoritma 2.B), serta enkripsi per-objek AES-256-CBC dengan vektor inisialisasi acak. Cipher Block Chaining (CBC) adalah mode kerahasiaan (NIST SP 800-38A). Vektor inisialisasinya harus tidak dapat diprediksi.

Vektor inisialisasi selalu dibuat baru untuk setiap objek dan setiap eksekusi, sehingga byte mentah berbeda antar-eksekusi. Karena itu, profil reproduksibilitasnya adalah structural. Sebelum membandingkan dua eksekusi, harness mengkanonikalisasi IV enkripsi, urutan objek, dan trailer /ID. Profil ini lebih ketat daripada profil untuk resep yang tidak menggunakan enkripsi.

Bitmask izin menentukan entri P. Bit 3 memberikan izin cetak, dan bit 6 memberikan izin annotation/form-fill. Nilainya adalah kuantitas 32-bit tak bertanda yang terdokumentasi.

NextPDF\Core\Concerns\HasSecurity (digabungkan ke dalam Document):

  • setEncryption(#[SensitiveParameter] string $userPassword, #[SensitiveParameter] string $ownerPassword = '', int $permissions = -1): static — mengonfigurasi enkripsi AES-256 standard-handler. permissions = -1 memberikan semua izin. Jika ownerPassword kosong, kata sandi pengguna digunakan kembali sebagai kata sandi pemilik. Panggil sebelum addPage().
  • getEncryptor(): ?Aes256Encryptor — enkriptor yang dikonfigurasi, atau null.
  • useAesGcm(?bool $enabled = true): static — mengaktifkan ISO/TS 32003 AES-256-GCM; melempar pengecualian jika host OpenSSL/libsodium tidak menyediakan cipher tersebut.

Kedua parameter kata sandi ditandai #[SensitiveParameter], sehingga PHP menyensor keduanya dari stack trace.

Bit izin (entri P, bit rendah 3–6 yang umum digunakan):

BitNilaiOperasi
34Mencetak dokumen
48Memodifikasi isi dokumen
516Menyalin / mengekstrak teks dan grafik
632Menambahkan atau memodifikasi anotasi dan mengisi field formulir
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();
$doc->setTitle('Confidential Memo');
// Grant printing only (bit 3 = 4). MUST run before addPage().
$doc->setEncryption(
userPassword: 'open-me',
ownerPassword: 'owner-secret',
permissions: 4,
);
$doc->addPage();
$doc->setFont('helvetica', '', 12);
$doc->cell(0, 10, 'Encrypted with AES-256; printing allowed only.', newLine: true);
$doc->save(__DIR__ . '/confidential.pdf');
echo "Wrote confidential.pdf\n";

Contoh lengkap di bawah ini mencerminkan examples/22-protection.php dan menulis ke NEXTPDF_COOKBOOK_OUTPUT untuk harness.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$userPassword = 'demo';
$ownerPassword = 'admin';
// Grant ONLY printing (bit 3 = 4); deny copy/modify/annotate.
$permissions = 4;
$doc = Document::createStandalone();
$doc->setTitle('Encrypted Document — Restricted Permissions');
$doc->setAuthor('NextPDF Example');
// setEncryption() MUST be called before addPage().
$doc->setEncryption(
userPassword: $userPassword,
ownerPassword: $ownerPassword,
permissions: $permissions,
);
$doc->addPage();
$doc->setFont('helvetica', 'B', 20);
$doc->cell(0, 14, 'Encrypted PDF Document', newLine: true);
$doc->ln(8);
$doc->setFont('helvetica', '', 11);
$doc->multiCell(0, 7, 'This document is protected with AES-256 encryption '
. '(standard security handler, revision 6). The user password is required '
. 'to open it; the owner password grants full access. The permission '
. 'bits below are honoured by conforming readers only.');
$doc->ln(5);
$permissionTable = [
['Bit 3 (4)', 'Printing', 'ALLOWED'],
['Bit 4 (8)', 'Content modification', 'DENIED'],
['Bit 5 (16)', 'Text copying / extraction', 'DENIED'],
['Bit 6 (32)', 'Annotations / form fields', 'DENIED'],
];
$doc->setFont('helvetica', 'B', 10);
$doc->cell(30, 7, 'Flag');
$doc->cell(60, 7, 'Operation');
$doc->cell(0, 7, 'Status', newLine: true);
foreach ($permissionTable as [$bit, $operation, $status]) {
$doc->setFont('courier', '', 9);
$doc->cell(30, 7, $bit);
$doc->setFont('helvetica', '', 10);
$doc->cell(60, 7, $operation);
$doc->setFont('helvetica', 'B', 10);
$doc->cell(0, 7, $status, newLine: true);
}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/encrypted.pdf');
echo "Wrote encrypted PDF (AES-256, printing only)\n";

Keluaran yang diharapkan:

Wrote encrypted PDF (AES-256, printing only)

Saat Anda membuka berkas tersebut, pembaca meminta kata sandi. Kata sandi pengguna membukanya dengan kumpulan izin yang dibatasi. Kata sandi pemilik membukanya dengan akses penuh.

  • Urutan pemanggilan. Memanggil setEncryption() setelah addPage() tidak mengenkripsi konten sebelumnya secara retroaktif. Konfigurasikan enkripsi terlebih dahulu; mesin mengenkripsi setiap badan objek saat ditulis.
  • Default kata sandi pemilik. Kata sandi pemilik yang kosong membuat mesin menggunakan kembali kata sandi pengguna sebagai kata sandi pemilik. Hal itu secara efektif tidak menyisakan peran tanpa hak istimewa. Tetapkan kata sandi yang berbeda jika kedua peran tersebut harus dipisahkan.
  • Semantik izin bersifat anjuran. Hanya pembaca yang patuh yang menghormati bit tersebut. Bit tersebut tidak ditegakkan secara kriptografis: alat yang tidak patuh, atau alat apa pun yang digunakan dengan kata sandi pemilik, dapat melakukan operasi yang dibatasi. Perlakukan izin sebagai sinyal kebijakan untuk perangkat lunak yang bekerja sama, jangan pernah sebagai kontrol akses yang tahan terhadap pihak yang gigih.
  • Tidak ada jaminan integritas. Enkripsi adalah kerahasiaan, bukan integritas. Penyerang tanpa kata sandi tidak dapat membaca konten, tetapi format itu sendiri tidak mendeteksi perusakan. Perlindungan integritas memerlukan mekanisme terpisah, seperti tanda tangan digital atau MAC dokumen ISO/TS 32004.
  • Konflik PDF/A. PDF/A melarang kunci trailer Encrypt. Memanggil setEncryption() pada dokumen PDF/A, dalam urutan apa pun, akan melempar pengecualian ketidakcocokan.
  • Aktivasi AES-256-GCM. useAesGcm() memilih enkripsi massal GCM ISO/TS 32003 ketika host OpenSSL atau libsodium menyediakannya. Jika tidak tersedia, ia melempar InvalidConfigException. Ia tidak kompatibel dengan PDF/A karena alasan yang sama.
  • Enkripsi kunci publik belum terhubung. setPublicKeyEncryption() membekukan permukaan API, tetapi save() akan melempar pengecualian hingga koneksi writer tersedia (cacat yang diketahui). Jangan gunakan di produksi pada Core.

Derivasi kunci menjalankan hash iteratif Algoritma 2.B sekali per dokumen. AES-256-CBC per-objek bersifat linear terhadap ukuran badan objek. Untuk dokumen umum, biayanya tetap jauh di dalam anggaran 1500 ms / 64 MB. Dokumen yang sangat besar menimbulkan biaya throughput AES per objek. Galois/Counter Mode (GCM) dengan AES-NI lebih cepat pada host yang mendukung.

  • Hanya kerahasiaan. Tegaskan kembali batas kepercayaan: enkripsi menjaga konten dari pihak yang tidak memiliki kata sandi. Enkripsi tidak membuktikan bahwa berkas tidak diubah, dan bit izin bergantung pada kerja sama pembaca.
  • Kekuatan kata sandi adalah tanggung jawab Anda. Handler hanya sekuat kata sandinya. Begitu seseorang memperoleh berkas, kata sandi pengguna yang lemah dapat dipecahkan secara offline; format tidak dapat membatasi laju percobaan.
  • Kata sandi pemilik adalah kunci utama. Siapa pun yang memiliki kata sandi pemilik melewati setiap pembatasan. Perlakukan seperti kredensial root; jangan pernah mengirimkannya bersama dokumen atau mencatatnya di log.
  • #[SensitiveParameter] adalah pertahanan berlapis. #[SensitiveParameter] menyensor kata sandi dari stack trace PHP, tetapi Anda tetap harus menjaganya agar tidak masuk ke log, pesan pengecualian, dan laporan crash Anda sendiri.

Pustaka menjalankan enkripsi dalam proses. Ia tidak mengirimkan dokumen atau kata sandi ke mana pun. Mesin tidak menulis kata sandi, kunci, atau byte dokumen apa pun ke disk kecuali keluaran terenkripsi yang Anda simpan. Lokasi berkas keluaran dan cara penyimpanan kata sandi adalah urusan deployment yang menjadi tanggung jawab integrator. Pustaka tidak memberikan jaminan residensi. Jika dokumen plaintext berisi data pribadi, data tersebut hanya seaman kata sandi yang paling lemah dan peringatan kerja sama pembaca di atas. Enkripsi bukanlah pengganti untuk meminimalkan informasi yang dapat mengidentifikasi pribadi (PII) yang Anda masukkan ke dalam dokumen.

Enkripsi menerbitkan EncryptionAppliedEvent yang hanya membawa nama algoritma (AES-256) dan tiga boolean yang merangkum apakah print/copy/modify diizinkan — tidak ada kata sandi, kunci, salt, atau IV yang pernah dimasukkan ke event tersebut (src/Event/Security/EncryptionAppliedEvent.php). Jalur OpenTelemetry merutekan atribut span melalui sanitizer allowlist (src/Telemetry/AttributeSanitizer.php) yang tanpa syarat menolak kata sandi dan jalur berkas; hanya kunci dalam allowlist dengan nilai skalar yang bertahan. Jangan menambahkan kata sandi atau materi kunci ke span, log, atau pesan pengecualian dalam kode integrasi Anda sendiri. Penanda #[SensitiveParameter] melindungi stack trace, tetapi bukan string yang Anda bangun sendiri.

Dalam cakupan: penyerang yang memperoleh berkas terenkripsi tetapi tidak memiliki kata sandi. Mereka tidak dapat membaca konten, bergantung pada kekuatan kata sandi, dan berkas tidak membocorkan plaintext. Di luar cakupan: penyerang yang memiliki kata sandi pengguna atau pemilik; pembaca yang tidak patuh yang mengabaikan bit izin; brute force offline terhadap kata sandi yang lemah; deteksi perusakan (enkripsi memberikan kerahasiaan, bukan integritas); side-channel pada build OpenSSL host; dan penyimpanan kunci, yang sepenuhnya menjadi tanggung jawab integrator. Mendokumentasikan ancaman-ancaman ini tidak menyatakan tidak adanya kerentanan.

Build OpenSSL pada host menyediakan primitif kriptografis, sehingga postur FIPS adalah properti host, bukan pengaturan pustaka. CryptoCapabilities::detectFipsMode() mengembalikan FipsModeDetection dengan tiga keadaan (src/Security/FipsModeDetection.php): FIPS_ACTIVE, FIPS_ABSENT, atau INDETERMINATE. Ekstensi openssl PHP tidak mengekspos binding untuk model provider OpenSSL 3, sehingga pemeriksaan ini bersifat upaya terbaik. INDETERMINATE diperlakukan sebagai “FIPS tidak terbukti” (fail-closed), yang dapat dibedakan dalam telemetri yang dapat ditindaklanjuti oleh operator. NextPDF tidak mengklaim validasi FIPS 140; menjalankan pada OpenSSL yang tervalidasi FIPS adalah tanggung jawab operator, dan hasil deteksi bersifat anjuran.

PernyataanSpecKlausulreference_id
Kode V pada kamus enkripsi memilih algoritma enkripsi.ISO 32000-2§7.6
Metode crypt-filter AESV3 dinamai oleh entri CFM.ISO 32000-2§7.6
Entri P adalah kuantitas izin-akses 32-bit tak bertanda.ISO 32000-2§7.6
Bit izin 3 mengontrol pencetakan.ISO 32000-2§7.6
Bit izin 6 mengontrol anotasi / pengisian formulir.ISO 32000-2§7.6
Enkripsi melindungi konten dari akses yang tidak sah (kerahasiaan).ISO 32000-2§7.6
Derivasi kunci revisi-6 menggunakan hashing iteratif ber-salt (Algoritma 2.B).ISO 32000-2§7.6
CBC adalah mode kerahasiaan (bukan mode integritas).NIST SP 800-38A§6.2
Vektor inisialisasi CBC harus tidak dapat diprediksi.NIST SP 800-38ALamp. C

NextPDF mengimplementasikan klausul yang dikutip. NextPDF tidak menyatakan kesesuaian menyeluruh ISO 32000-2, validasi FIPS 140, atau jaminan kerahasiaan hukum maupun kontraktual apa pun. “Dukungan untuk security handler standar” bukanlah sertifikasi keamanan dalam deployment Anda. Hal tersebut bergantung pada penyimpanan kata sandi dan kebijakan verifier di luar pustaka.