Lewati ke konten

Keamanan dan operasi Artisan

Jembatan ini merender HTML yang mungkin tidak tepercaya di dalam Chrome, dengan dua penghalang jaringan independen dan kebijakan konten yang ketat. Sandbox tingkat sistem operasi milik Chrome merupakan kontrol terpisah dan opsional dengan batasan yang eksplisit. Halaman ini mendokumentasikan batasan tersebut, tanpa mengklaim bahwa batasnya absolut.

Sebuah render merupakan eksekusi permintaan di sisi server: aplikasi Anda menyerahkan HTML ke mesin peramban yang secara baku dapat mengambil sumber daya. Ketika input yang tidak tepercaya mengendalikan pengambilan ke luar, risikonya adalah server-side request forgery (SSRF): entri Common Weakness Enumeration (CWE) CWE-918 mendefinisikannya sebagai kondisi ketika server mengambil konten dari URL yang diberikan tanpa memastikan secara memadai bahwa permintaan tersebut mencapai tujuan yang diharapkan. SSRF (CWE-918) adalah kelemahan CWE Top 25. Open Worldwide Application Security Project (OWASP) Application Security Verification Standard (ASVS) mengharuskan Anda mengendalikan permintaan keluar dari komponen server alih-alih membiarkannya implisit. OWASP SSRF Prevention Cheat Sheet memperlakukan penolakan di lapisan jaringan terhadap panggilan ke tujuan sembarang sebagai kontrol yang kuat. Postur jaringan tolak-secara-baku di bawah ini merupakan respons jembatan terhadap persyaratan tersebut. National Institute of Standards and Technology (NIST) Special Publication (SP) 800-53 SC-7 menjelaskan prinsip batas tolak-semua-izinkan-melalui-pengecualian yang sama, yang diterapkan jembatan di lapisan transport.

HTML yang diteruskan ke jembatan diproses sepenuhnya di dalam proses dan instans Chrome lokal. Jembatan ini tidak melakukan panggilan jaringan keluar sendiri dan mencegah Chrome melakukan panggilan apa pun (lihat model jaringan di bawah), sehingga konten input tidak meninggalkan host melalui renderer. Personally identifiable information (PII) di dalam input dirender menjadi output Portable Document Format (PDF) yang Anda hasilkan, jadi perlakukan output dengan kontrol residensi yang sama seperti input. Jembatan ini tidak menyimpan input atau output ke disk; persistensi menjadi tanggung jawab pemanggil.

ChromeHtmlRenderer dan BrowserPool menerima PHP Standard Recommendation (PSR)-3 LoggerInterface opsional. Jembatan ini hanya mencatat metadata operasional: panjang byte input, lebar dan tinggi target, panjang byte output, tinggi konten terukur, peluncuran peramban beserta path biner yang dikonfigurasi, pemberitahuan mulai ulang dengan jumlah render, dan peristiwa penutupan. Jembatan ini tidak mencatat konten HTML, byte yang dirender, atau teks yang diekstraksi. Hal ini selaras dengan panduan NIST SP 800-92 untuk mencatat peristiwa operasional sembari menjauhkan payload sensitif dari log. Path biner dicatat; perlakukan ini sebagai metadata deployment yang tidak sensitif. Pengujian memverifikasi bentuk pemanggilan log di tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize dan tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.

Model isolasi jaringan (pertahanan berlapis)

Bagian berjudul “Model isolasi jaringan (pertahanan berlapis)”

Jembatan ini menerapkan dua penghalang independen sehingga jika satu lapisan terlewati, host tidak langsung terekspos:

  1. Content-Security-Policy. Setiap render dibungkus oleh ChromeSecurityPolicy::wrapHtml() ke dalam dokumen yang membawa:

    default-src 'none'; style-src 'unsafe-inline'; img-src data:;
    base-uri 'none'; form-action 'none'; frame-ancestors 'none';
    navigate-to 'none';

    Direktif Content Security Policy (CSP) default-src 'none' menolak semua origin sumber daya. img-src data: hanya mengizinkan gambar inline. navigate-to 'none' memblokir navigasi sisi klien. style-src 'unsafe-inline' adalah satu-satunya pelonggaran yang diperlukan agar Chrome printToPDF dapat menerapkan gaya inline. Hal ini diverifikasi di src/Artisan/ChromeSecurityPolicy.php dan ditegaskan oleh ChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives.

  2. Pemblokiran transport Chrome DevTools Protocol (CDP). Sebelum konten dimuat, ChromeHtmlRenderer mengirim Network.enable lalu Network.setBlockedURLs dengan pola ['*']. Ini memblokir setiap URL subsumber daya di lapisan transport Chrome DevTools Protocol, terlepas dari CSP. Hal ini diverifikasi di src/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests() dan ditegaskan oleh ChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests (yang memeriksa urutan metode CDP yang tepat dan parameter ['urls' => ['*']]). Ini adalah pemblokiran di lapisan jaringan yang direkomendasikan panduan OWASP SSRF sebagai kontrol terkuat, dan merupakan penolakan-semua di tingkat transport yang konsisten dengan NIST SP 800-53 SC-7.

Hasilnya: URL <img>, lembar gaya, fon, skrip, atau iframe jarak jauh di dalam input tidak dimuat. Jembatan ini tidak menerapkan daftar izin domain atau filter IP privat karena memang tidak membutuhkannya: jembatan ini tidak mengizinkan pengambilan subsumber daya keluar sama sekali.

Catatan drift: docblock nextpdf/core pada writeHtmlChrome() menyatakan Chrome “akan mengambil sumber daya eksternal” dan menyarankan mengonfigurasi kebijakan untuk “memblokir rentang IP privat dan membatasi domain yang diizinkan.” Pernyataan itu menggambarkan model daftar izin yang dapat dikonfigurasi. ChromeSecurityPolicy Artisan yang dikirimkan tidak mengekspos daftar izin; jembatan ini memblokir semua permintaan subsumber daya tanpa syarat. Yang otoritatif adalah kode, bukan docblock core. Drift ini dicatat untuk tim dokumentasi core.

ChromeSecurityPolicy::validate() dijalankan sebelum jembatan menghubungi Chrome dan menolak:

PemeriksaanBatasAlasan
Ukuran HTML> maxHtmlSize (baku 5 MB)Batas untuk penipisan sumber daya (uncontrolled resource consumption CWE Top 25)
Data URI base64grup tangkap >= 13_000_000 byteBatas bom dekompresi
<meta http-equiv="refresh">apa pun (tidak peka huruf besar/kecil, single/double quote)Memblokir pengalihan sisi klien ke endpoint internal, yang merupakan vektor navigasi SSRF

Pemblokiran meta-refresh adalah pengerasan SSRF yang eksplisit. Tanpanya, HTML yang dikendalikan penyerang dapat mengalihkan Chrome ke endpoint metadata cloud sebelum printToPDF. Perilaku batas ini ditegaskan dalam ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).

Selain itu, ChromeSecurityPolicy::wrapHtml() menghapus </style> dari defaultCss sebelum injeksi untuk mencegah blok gaya keluar ke konteks skrip (ditegaskan oleh ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).

Batas sandbox Chrome — dinyatakan secara eksplisit

Bagian berjudul “Batas sandbox Chrome — dinyatakan secara eksplisit”

Sandbox tingkat sistem operasi milik Chrome merupakan kontrol yang terpisah dari penghalang jaringan di atas, dan jembatan ini tidak menjaminnya.

  • Secara baku, noSandbox bernilai false, sehingga Chrome diluncurkan dengan sandbox-nya sendiri aktif. Jembatan ini tidak mengimplementasikan sandbox tersebut; ia mengandalkan sandbox dari biner Chrome, yang bergantung pada dukungan kernel host.
  • Menyetel noSandbox: true meluncurkan Chrome dengan --no-sandbox. Ini menghapus sandbox isolasi-proses Chrome. Opsi ini disediakan untuk kontainer di mana sandbox tidak dapat diinisialisasi. Ini merupakan pengurangan isolasi yang nyata: kompromi renderer tidak lagi ditampung oleh sandbox Chrome.
  • Penghalang jaringan jembatan (CSP + blokir CDP) tetap berlaku, baik sandbox aktif maupun tidak, tetapi keduanya bukan pengganti isolasi proses. Panduan least-privilege OWASP ASVS berlaku: jalankan Chrome sebagai pengguna non-root, di dalam kontainer yang terbatas, dengan noSandbox hanya jika tidak terhindarkan, dan perlakukan deployment --no-sandbox sebagai persyaratan kepercayaan yang lebih tinggi terhadap input.

Dokumentasi ini tidak mengklaim bahwa jembatan “aman secara baku” atau “tahan rusak”. Dokumentasi ini juga tidak mengklaim bahwa menonaktifkan sandbox itu aman. Dokumentasi ini menyatakan kontrol yang ada dan batas berhentinya kontrol tersebut. Penyediaan kontainer yang mendukung sandbox dibahas di halaman /integrations/artisan/chrome-renderer-setup/.

Mode kegagalan ini diuraikan berdasarkan src/Artisan/Exception/ dan kode render/transport:

KondisiMuncul sebagaiSumber
Pustaka chrome-php/chrome tidak adaChromeNotAvailableException (dengan perintah pemasangan)BrowserPool::getBrowser()
HTML melebihi maxHtmlSizeRuntimeException (“exceeds maximum allowed size”)ChromeSecurityPolicy::validate()
Data URI base64 berukuran berlebihRuntimeException (“oversized base64 data URI”)ChromeSecurityPolicy::validate()
Meta-refresh terlarangRuntimeException (“forbidden meta refresh redirect”)ChromeSecurityPolicy::validate()
Peluncuran / batas waktu / crash ChromeChromeRenderException (membungkus penyebabnya)ChromeHtmlRenderer::render()
Chrome mengembalikan PDF kosongChromeRenderException (“returned empty data”)ChromeHtmlRenderer::render()
Halaman tidak memiliki content streamPdfParseExceptionPageImporter::import()

Jika ChromeRenderException dilempar selama render, exception tersebut dilempar ulang tanpa perubahan. Setiap Throwable lain dibungkus sebagai ChromeRenderException, dengan mempertahankan exception sebelumnya (ditegaskan oleh ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping dan ::renderWrapsUnexpectedThrowablesWithChromeRenderException). Halaman Chrome selalu ditutup di dalam blok finally, bahkan saat gagal.

  • Ukuran input: maxHtmlSize (baku 5 MB) dan batas data URI base64 sebesar 13 MB.
  • Waktu: renderTimeout detik membatasi pemuatan konten maupun panggilan CDP sinkron. Perintah kontrol CDP menggunakan batas waktu tetap 5 detik.
  • Proses: BrowserPool memulai ulang Chrome setiap 100 render untuk membatasi pertumbuhan memori dan menutup proses pada close() / penghancuran.

Ini adalah batas, bukan kuota. Untuk setiap jalur yang terekspos ke input tidak tepercaya, tetap gunakan batas sumber daya tingkat host (cgroup, ulimit, anggaran permintaan), konsisten dengan panduan konsumsi sumber daya CWE Top 25.

Suntikkan logger PSR-3 untuk menangkap awal render (ukuran, lebar, tinggi), render selesai (ukuran output, tinggi konten), peluncuran peramban (path biner), mulai ulang peramban (jumlah render), dan penutupan peramban (jumlah render). Ini adalah satu-satunya peristiwa yang dipancarkan, dan semuanya tidak membawa konten payload. Gunakan peristiwa tersebut untuk service-level objective (SLO) latensi dan peringatan laju mulai ulang.

KlaimReferensiclause_idreference_id
Permintaan keluar dari komponen server harus dikendalikanOWASP ASVS 5.0§ (SSRF/kontrol keluar)
SSRF = server mengambil URL yang diberikan tanpa memvalidasi tujuannyaCWE Top 25 2025 (CWE-918)cwe_top25_2025#x28.x2.p2
SSRF (CWE-918) adalah kelemahan CWE Top 25CWE Top 25 2025cwe_top25_2025#x1.p73
Konsumsi sumber daya tak terkendali adalah kelemahan CWE Top 25CWE Top 25 2025 (CWE-400)cwe_top25_2025#x19.x2.p2
Perlindungan batas tolak-secara-baku (izinkan melalui pengecualian)NIST SP 800-53 Rev 5 SC-7SC-7
Penolakan di lapisan jaringan terhadap panggilan ke tujuan sembarang adalah kontrol SSRF yang kuatOWASP Cheat Sheet Series (SSRF Prevention §Network layer)owasp_cheatsheet_series#x132.x2
Lindungi komponen pengambil-URL terhadap SSRFOWASP Cheat Sheet Series§ (pencegahan SSRF, perkakas pengambil-URL)
Isolasi renderer untuk konten tidak tepercaya, least privilegeOWASP ASVS 5.0§ (sandbox / least privilege)
Catat peristiwa operasional; jauhkan payload dari logNIST SP 800-92§ (panduan konten log)

Rujukan diambil melalui mesin kepatuhan NextPDF (manifes korpus 1d05b7c4…d790b6); teks klausa diparafrasekan, tidak pernah dikutip langsung.

AncamanKontrolRisiko residual
SSRF melalui subsumber daya jarak jauhCSP default-src 'none' + CDP setBlockedURLs('*')Bug pada mesin Chrome yang melewati kedua penghalang (pertahanan berlapis menurunkan risiko, tetapi tidak menghilangkannya)
SSRF melalui navigasi meta-refreshValidasi pra-Chrome menolak tag tersebutVektor navigasi baru yang tidak cocok dengan pola tersebut
Penipisan sumber dayaBatas ukuran input + batas base64 + batas waktu + mulai ulang setiap 100 renderTidak ada kuota per host; padukan dengan cgroup/ulimit
Kompromi proses rendererSandbox Chrome saat aktifnoSandbox: true menghapus kontrol ini sepenuhnya
Pelarian gaya / injeksiPenghapusan </style> di defaultCss; CSP memblokir skripInjeksi melalui vektor di masa depan yang tidak dihapus

Jembatan ini tidak melakukan operasi kriptografis apa pun. Ia menghasilkan byte PDF melalui Chrome dan menyematkannya. Penandatanganan, enkripsi, dan perilaku mode Federal Information Processing Standards (FIPS) menjadi urusan core/Premium dan tidak terpengaruh oleh Artisan.

  • /integrations/artisan/configuration/
  • /integrations/artisan/chrome-renderer-setup/
  • /integrations/artisan/troubleshooting/
  • /integrations/artisan/production-usage/
  • /integrations/artisan/overview/