Keamanan dan operasi
Sekilas pandang
Bagian berjudul “Sekilas pandang”Jembatan ini mengirimkan Hypertext Markup Language (HTML) Anda melewati batas jaringan menuju mesin peramban. Halaman ini mendokumentasikan kontrol yang melindungi batas tersebut, dengan kode sumber sebagai acuan kebenaran. Ketika sebuah kontrol mengutip suatu standar, kutipan itu adalah kutipan yang dideklarasikan dalam docblock kode. Halaman ini menyatakan ulang penegasan kode; halaman ini tidak menyusun ulang rumusan normatif.
Model ancaman
Bagian berjudul “Model ancaman”Docblock paket ini menyebutkan ancaman yang ditangkal:
- XSS-ke-PDF — Cross-site scripting (XSS) melalui markup berbahaya yang dieksekusi selama proses render Portable Document Format (PDF).
- SSRF — Server-side request forgery (SSRF) akibat markup atau Uniform Resource Locator (URL) tujuan yang mengirim permintaan ke alamat internal.
- Penghabisan sumber daya — Input yang terlalu besar atau bom dekompresi.
- Rebinding DNS — rebinding Domain Name System (DNS), ketika sebuah hostname lolos validasi lalu dipetakan ke alamat privat saat koneksi dilakukan.
- Penyadapan TLS on-path — penyadapan Transport Layer Security (TLS) on-path melalui sertifikat yang diganti pada jalur menuju Worker.
Setiap ancaman memiliki kontrol spesifik yang dapat diuji di bawah ini.
Kontrol input (sebelum permintaan meninggalkan PHP)
Bagian berjudul “Kontrol input (sebelum permintaan meninggalkan PHP)”CloudflareSecurityPolicy::validate() berjalan sebelum permintaan apa pun dibuat:
| Kontrol | Perilaku | Sumber batas |
|---|---|---|
| Batas ukuran | Menolak HTML yang lebih besar daripada maxHtmlSize | CloudflareRendererConfig, baku 5000000 bita |
| Pelindung bom-dekompresi Base64 | Memperkirakan ukuran hasil dekode dari setiap URI data:…;base64,…; menolak nilai yang mencapai atau melampaui batas atas | MAX_DATA_URI_BYTES = 13631488 |
| Larangan meta-refresh | Menolak setiap <meta http-equiv="refresh">, tanpa membedakan huruf besar-kecil | regex di CloudflareSecurityPolicy |
Pelanggaran memunculkan RuntimeException dengan pesan yang menyebutkan nilai yang melanggar beserta batasnya. Larangan meta-refresh ada karena direktif refresh dapat memulai navigasi di dalam halaman yang dirender Worker — vektor SSRF yang berada di dalam konten, bukan di URL.
Kebijakan keamanan HTML dari nextpdf/core (HtmlSecurityPolicyInterface, baku DefaultHtmlSecurityPolicy) berjalan di lapisan penguraian dan melengkapi pemeriksaan lapisan transpor di atas. Ambil kebijakan ini dengan getHtmlSecurityPolicy(). Suntikkan kebijakan kustom melalui konstruktor.
Kontrol tujuan (SSRF dan rebinding DNS)
Bagian berjudul “Kontrol tujuan (SSRF dan rebinding DNS)”CloudflareSecurityPolicy::validateWorkerUrl():
- Menolak URL yang gagal diurai atau tidak memiliki scheme/host (
Invalid Worker URL). - Menolak skema apa pun selain HTTPS (
Worker URL must use HTTPS). - Untuk host berupa IP literal, menolak rentang privat atau yang dicadangkan dengan
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGEmilik PHP. Pada praktiknya, hal ini menolak ruang privat RFC 1918, loopback, dan alamat link-local RFC 3927. Pengujian secara eksplisit mencakup penolakan192.168.x,127.0.0.1, dan169.254.x. Keanggotaan rentang ditentukan oleh ekstensi filter PHP; paket ini tidak menyematkan keputusan tersebut ke suatu klausa. RFC 1918 dan RFC 3927 disebutkan di sini secara deskriptif sebagai definisi yang lazim dikenal untuk rentang-rentang tersebut. - Untuk hostname, memetakan semua rekaman A dan AAAA dengan
dns_get_record()(bukangethostbyname(), yang hanya mengembalikan jawaban pertama) dan menolak host jika ada alamat hasil pemetaan yang bersifat privat atau dicadangkan.
Pemetaan seluruh rekaman digunakan dengan sengaja. Docblock kelas mendokumentasikannya sebagai pertahanan terhadap host yang mengembalikan beberapa rekaman, ketika pencarian satu rekaman mungkin memilih alamat publik sementara koneksi berikutnya memilih alamat privat. Hal ini sejalan dengan OWASP SSRF Prevention Cheat Sheet: petakan jawaban A maupun AAAA untuk domain dan terapkan pemeriksaan alamat nonpublik pada seluruh set hasil.
validateWorkerUrl() mengembalikan set IP yang telah diperiksa. Tepat sebelum mengirim permintaan, renderer memanggil assertPinsStillValid(). Panggilan itu memetakan ulang host dan menolak IP yang baru muncul (Worker URL DNS answer changed since validation — possible DNS rebinding attack). Hal ini menutup celah time-of-check / time-of-use antara validasi dan koneksi.
Kontrol transpor (PinnedCurlTransport)
Bagian berjudul “Kontrol transpor (PinnedCurlTransport)”Ketika set IP yang telah diperiksa atau set pin Subject Public Key Info (SPKI) tersedia dan sebuah ResponseFactory PHP Standards Recommendation 17 (PSR-17) disediakan, renderer menggunakan Transport\PinnedCurlTransport alih-alih klien PHP Standards Recommendation 18 (PSR-18) yang disuntikkan. Transpor menegakkan kontrol berikut pada lapisan handle cURL:
- DNS yang disematkan —
CURLOPT_RESOLVEmengikat host:port ke set IP yang telah diperiksa, sehingga libcurl tidak melakukan pencarian sendiri saat koneksi. Pengikatan ini membuat pemeriksaan DNS di sisi userland berlaku pada koneksi yang sebenarnya; tanpanya, libcurl bisa memetakan ke alamat yang berbeda. - Penyematan kunci publik TLS —
CURLOPT_PINNEDPUBLICKEYdiatur dari set pin gabungan. Hal ini mengikuti RFC 7469 §2.6: koneksi dengan pin diterima ketika set sidik jari SPKI yang disajikan server beririsan dengan set pin yang dikonfigurasi, dan kegagalan validasi pin tidak dapat dipulihkan. String pin dinormalisasi dari bentuksha256/<base64>ke bentuksha256//<base64>milik cURL; pin yang cacat memunculkanInvalidSpkiPinException. - Verifikasi TLS aktif —
CURLOPT_SSL_VERIFYPEER => true,CURLOPT_SSL_VERIFYHOST => 2. - Tanpa pengalihan otomatis —
CURLOPT_FOLLOWLOCATION => false,CURLOPT_MAXREDIRS => 0. Respons 3xx dimunculkan ke lapisan kebijakan, bukan diikuti oleh libcurl menuju host yang belum diperiksa. Docblock kelas menyatakan bahwa ini disengaja, sehingga pengalihan divalidasi ulang alih-alih diikuti secara diam-diam. - Batas waktu tegas —
CURLOPT_TIMEOUTdiatur darirenderTimeout(baku30detik).
Kesalahan cURL atau body yang bukan string memunculkan CloudflareRenderException beserta nomor dan pesan kesalahan cURL.
Panduan operasional penyematan
Bagian berjudul “Panduan operasional penyematan”Konfigurasi memuat pinnedPublicKeys dan backupPublicKeys secara terpisah. RFC 7469 §2.5 menjelaskan pin cadangan sebagai sidik jari untuk pasangan kunci sekunder yang belum di-deploy dan disimpan secara offline, serta memperlakukannya sebagai jalur pemulihan utama untuk kegagalan validasi pin yang tidak disengaja. Simpan setidaknya satu pin cadangan agar rotasi sertifikat tidak membuat endpoint tidak dapat diakses. Field terpisah memungkinkan Anda memvalidasi rotasi secara independen. Secara operasional:
- Sematkan SPKI dari sertifikat leaf atau dari sertifikat antara yang rotasinya Anda kendalikan.
- Selalu konfigurasikan pin cadangan untuk sertifikat berikutnya sebelum melakukan rotasi.
- Set pin kosong menonaktifkan penyematan; gunakan itu hanya dengan rantai sertifikat yang stabil dan diketahui. Penyematan bersifat opt-in melalui konfigurasi.
Autentikasi dan penanganan rahasia
Bagian berjudul “Autentikasi dan penanganan rahasia”- Permintaan Worker membawa
Authorization: Bearer <apiToken>.apiTokenber-#[SensitiveParameter], sehingga stack trace menyensornya. Probe keterjangkauan mengirim header bearer yang sama pada Hypertext Transfer Protocol (HTTP)HEAD. - Kunci akses Cloudflare R2 (
accessKeyId,secretAccessKey) ber-#[SensitiveParameter]dan hanya digunakan untuk menurunkan kunci penandatanganan Amazon Web Services (AWS) Signature V4. ApiKeyValidatormembandingkan kunci denganhash_equals()(aman terhadap timing) dan mendukung penyimpanan kunci yang sudah di-hash Secure Hash Algorithm 256 (SHA-256) melaluivalidateHashed().- Objek konfigurasi bersifat
final readonly— rahasia yang sudah disetel sekali tidak dapat diubah. - Ambil rahasia dari variabel lingkungan atau pengelola rahasia. Jangan pernah meng-commit rahasia tersebut. Paket ini mengikuti baseline keamanan NextPDF yang lebih luas: PHPStan Level 10,
declare(strict_types=1)pada setiap berkas, tanpaeval()/exec(), GitHub Actions disematkan ke SHA.
Apa yang tidak ditegaskan oleh paket ini
Bagian berjudul “Apa yang tidak ditegaskan oleh paket ini”- Paket ini tidak menyatakan batas platform Cloudflare apa pun (waktu CPU Worker, memori, batas atas body permintaan, atau jumlah subpermintaan). Satu-satunya batas ukuran dan waktu yang dinyatakan dokumentasi ini adalah batas yang ditegakkan sendiri oleh paket, sebagaimana tercantum di atas dan di /integrations/cloudflare/configuration/. Untuk batas platform, rujuk dokumentasi resmi Cloudflare dan implementasi Worker Anda sendiri.
- Paket ini tidak menandatangani PDF dan tidak membuat klaim kesesuaian tanda tangan. Ketika tanda tangan diperlukan, render di sini, lalu tandatangani dengan mesin penandatanganan Anda. NextPDF Pro hanya menyediakan penandatanganan PDF Advanced Electronic Signatures (PAdES) B-B; profil long-term-validation merupakan kapabilitas Enterprise dan berada di luar cakupan jembatan ini.
- Paket ini tidak menyertifikasi, menjamin, atau menjadikan pipeline “tamper-proof”. Paket ini hanya mengimplementasikan kontrol spesifik yang dapat diverifikasi dari kode sumber seperti yang dijelaskan pada halaman ini.
Runbook operasional
Bagian berjudul “Runbook operasional”| Gejala | Pemeriksaan pertama |
|---|---|
Worker URL must use HTTPS | Periksa skema workerUrl yang dikonfigurasi. |
private or reserved IP | Periksa rekaman DNS dari hostname Worker; cari rekaman yang memetakan ke ruang RFC 1918 / loopback / RFC 3927. |
DNS answer changed since validation | Ketidakstabilan DNS atau upaya rebinding; petakan ulang dan periksa seluruh set rekaman. |
cURL transport error | Jalur jaringan, rantai TLS, dan — jika pin disetel — apakah SPKI dari sertifikat yang disajikan masih ada dalam set pin. |
| Render gagal tepat setelah rotasi sertifikat | Set pin tanpa pin cadangan yang cocok. Tambahkan SPKI baru sebagai cadangan sebelum melakukan rotasi. |
is not installed / no LocalRendererFactoryInterface | Fallback diaktifkan tetapi tidak ada factory yang terhubung, atau nextpdf/artisan tidak ada. |
| Penolakan rate limit tidak konsisten antarnode | Limiter in-memory bersifat per-proses; tempatkan penyimpanan bersama di depannya. |
Pelaporan insiden
Bagian berjudul “Pelaporan insiden”Laporkan kerentanan melalui GitHub Security Advisories atau kontak keamanan di SECURITY.md repositori. Jangan melaporkan isu keamanan sebagai GitHub issue publik.
Lihat juga
Bagian berjudul “Lihat juga”- /integrations/cloudflare/overview/ — mengapa paket ini dirancang seputar batas tersebut.
- /integrations/cloudflare/configuration/ — field untuk set pin dan batas.
- /integrations/cloudflare/troubleshooting/ — pemetaan lengkap kegagalan ke eksepsi.