Lewati ke konten

Contracts / Kebijakan Keamanan

Domain security-policy mendefinisikan tiga contract yang menolak secara default: CryptoPolicyInterface mengendalikan pilihan algoritme dan kunci, HtmlSecurityPolicyInterface membatasi permukaan fitur Hypertext Markup Language (HTML), dan ExternalResourcePolicyInterface mengatur pemuatan sumber daya jarak jauh. Karena semuanya berupa contract, Anda dapat menyediakan kebijakan deployment yang lebih ketat tanpa melakukan fork.

Terminal window
composer require nextpdf/core:^3

CryptoPolicyInterface adalah gerbang kriptografi. Core memanggilnya sebelum setiap tahap penandatanganan, enkripsi, atau hashing. Pemeriksaan ini mencakup hash, object identifier (OID) tanda tangan, cipher, dan kekuatan kunci. Contract ini juga melaporkan hash minimum dan nama kebijakan untuk log audit. Gunakan contract ini untuk menerapkan kumpulan aturan seperti Federal Information Processing Standards (FIPS) 140-3 atau eIDAS. Kode penandatanganan dan enkripsi tetap tidak berubah. Jika tidak ada kebijakan yang ditetapkan, semua algoritme diizinkan. Situs yang teregulasi harus menetapkan kebijakan secara eksplisit.

HtmlSecurityPolicyInterface bekerja di lapisan parsing HTML. Interface ini berjalan sebelum konten mencapai renderer mana pun. Interface ini menentukan apakah suatu tag, atribut, properti Cascading Style Sheets (CSS), atau skema uniform resource locator (URL) diizinkan. Interface ini juga membatasi ukuran input dan kedalaman penyarangan. Interface ini bekerja sama dengan kebijakan transport per-renderer (Chrome, Cloudflare, Gotenberg), yang menetapkan batas ukuran dan header Content Security Policy (CSP). Kebijakan HTML mengurangi permukaan serangan di lapisan parsing. Tag yang sudah dihapus tidak pernah mencapai tata letak. Elemen yang disisipkan tidak dapat mengubah keluaran. Jika tidak ada kebijakan yang ditetapkan, pengaturan bawaan mengizinkan seluruh rangkaian fitur.

ExternalResourcePolicyInterface menentukan apakah pipeline HTML boleh mengambil fon, stylesheet, atau gambar eksternal. Interface ini juga menetapkan batas untuk setiap pengambilan. Postur bawaannya adalah menolak semua. Setiap opsi tetap nonaktif hingga Anda mengaktifkannya. Contract ini menganut prinsip hak istimewa paling sedikit. HTML yang tidak tepercaya dapat menunjuk ke URL yang dikendalikan penyerang. Interface ini mengendalikan pengambilan @font-face berdasarkan skema, ukuran, dan jumlah glif. Interface ini mengendalikan @import berdasarkan skema, kedalaman, dan ukuran total. Interface ini mengendalikan background-image berdasarkan daftar skema dan allowlist domain dengan pencocokan persis. Interface ini membatasi ukuran data-URI (Uniform Resource Identifier). Interface ini juga mengendalikan referensi eksternal Scalable Vector Graphics (SVG). Contract ini menyatakan bahwa lingkungan produksi harus selalu menolaknya. Referensi tersebut memungkinkan pemalsuan permintaan dan injeksi skrip. Pengambilan URL yang terbuka merupakan jalur server-side request forgery. Kontrol akses dilewati dengan mengubah URL, sebagaimana diuraikan oleh Open Worldwide Application Security Project (OWASP) Top 10 2025. Pengadaan komponen eksternal harus dibatasi pada sumber resmi dan transport yang aman.

TipeJenisAnggota utamaStabilitasSejak
CryptoPolicyInterfaceinterfaceisHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName()stabil1.9.0
HtmlSecurityPolicyInterfaceinterfaceisTagAllowed(), isAttributeAllowed(), isCssPropertyAllowed(), isUrlSchemeAllowed(), getMaxInputSize(), getMaxNestingDepth(), getName()stabil3.1.0
ExternalResourcePolicyInterfaceinterfaceisFontFaceAllowed(), getAllowedFontSchemes(), getMaxFontFileSize(), getMaxFontGlyphs(), isImportAllowed(), getMaxImportDepth(), isBackgroundImageAllowed(), getAllowedImageDomains(), getMaxDataUrlSize(), isSvgExternalReferenceAllowed()stabil4.0.0

ExternalResourcePolicyInterface mengembalikan batas bertipe: ukuran positive-int, kedalaman import int<1, 100>, serta daftar skema dan domain list<non-empty-string>. Implementasi bawaan menolak setiap kapabilitas.

examples/contracts/security-policy-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
/**
* Decide whether a tag survives the policy.
*
* @param HtmlSecurityPolicyInterface $policy A core or custom policy.
*/
function tagSurvives(HtmlSecurityPolicyInterface $policy, string $tag): bool
{
return $policy->isTagAllowed($tag);
}

Fungsi ini hanya bergantung pada contract. Kebijakan yang restriktif maupun kebijakan bawaan sama-sama memenuhi contract tersebut.

examples/contracts/security-policy-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;
use NextPDF\Contracts\ExternalResourcePolicyInterface;
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
use Psr\Log\LoggerInterface;
final readonly class UntrustedHtmlGate
{
public function __construct(
private HtmlSecurityPolicyInterface $htmlPolicy,
private ExternalResourcePolicyInterface $resourcePolicy,
private CryptoPolicyInterface $cryptoPolicy,
private LoggerInterface $logger,
) {}
/**
* Reject input that exceeds the configured limits before rendering.
*
* @param string $html Untrusted HTML markup.
*/
public function assertAcceptable(string $html): void
{
$maxInput = $this->htmlPolicy->getMaxInputSize();
if ($maxInput > 0 && \strlen($html) > $maxInput) {
$this->logger->warning('HTML rejected: input over limit', [
'policy' => $this->htmlPolicy->getName(),
'limit' => $maxInput,
]);
throw new \LengthException('HTML input exceeds policy limit.');
}
if ($this->resourcePolicy->isSvgExternalReferenceAllowed()) {
$this->logger->error('Unsafe policy: SVG external references enabled.');
throw new \LogicException('SVG external references must be denied in production.');
}
}
}

Gerbang ini menegakkan batas input dan menolak kebijakan sumber daya yang tidak aman sebelum pipeline berjalan. Gerbang ini mencatat nama kebijakan untuk audit dan melempar exception yang spesifik.

  • CryptoPolicyInterface mengizinkan setiap algoritme jika tidak ada kebijakan yang ditetapkan. Pengaturan bawaan yang terbuka adalah kemudahan untuk pengembangan, bukan postur produksi. Tetapkan kebijakan secara eksplisit pada setiap deployment yang teregulasi.
  • HtmlSecurityPolicyInterface::getMaxInputSize() mengembalikan 0 untuk tanpa batas. Perlakukan 0 sebagai “tanpa batas dari kebijakan”, bukan “tolak semua”. Terapkan juga batas pada lapisan transport.
  • ExternalResourcePolicyInterface secara default menolak semua. Mengaktifkan @font-face atau background-image tanpa menetapkan daftar skema membuka permukaan serangan pemalsuan permintaan; tetapkan allowlist pada saat yang sama.
  • Allowlist domain yang kosong pada getAllowedImageDomains() berarti semua domain diizinkan setelah gambar latar belakang diaktifkan. Daftar kosong bukan aturan penolakan; sediakan domain secara eksplisit.
  • isSvgExternalReferenceAllowed() harus mengembalikan false di lingkungan produksi. Contract mendokumentasikan hal ini; kebijakan yang mengembalikan true adalah temuan, bukan pilihan konfigurasi.

Pemeriksaan kebijakan berupa pemanggilan predikat: O(1), tanpa biaya yang sebanding dengan ukuran input. Kebijakan dirujuk per tag, per atribut, per properti CSS, dan per URL selama parsing. Dokumen patologis melipatgandakan jumlah pemanggilan, tetapi setiap pemanggilan tetap berjalan dalam waktu konstan. performance_budget sebesar 1500 ms wall dan puncak 64 MB didominasi oleh parsing dan rendering, bukan oleh evaluasi kebijakan. Batas ukuran input dan kedalaman penyarangan membatasi biaya parser itu sendiri. Kebijakan yang ketat meningkatkan kinerja kasus terburuk dengan menolak dokumen yang berukuran berlebih atau memiliki penyarangan dalam sebelum tata letak.

Contract ini membentuk perimeter pertahanan engine, sehingga model ancamannya dibuat eksplisit. CryptoPolicyInterface memitigasi downgrade algoritme dengan memblokir hash yang lemah dan kunci yang pendek sebelum operasi apa pun. HtmlSecurityPolicyInterface memitigasi cross-site scripting yang memengaruhi keluaran Portable Document Format (PDF) dan injeksi konten dengan menghapus tag, atribut, dan CSS yang tidak diizinkan pada lapisan parsing sebelum renderer berjalan. ExternalResourcePolicyInterface memitigasi server-side request forgery, bom dekompresi, dan bom ukuran kumulatif dengan menolak semua secara default dan membatasi setiap pengambilan berdasarkan skema, ukuran, kedalaman, dan domain. Batas ukuran input, kedalaman penyarangan, glif fon, dan kedalaman import memitigasi penghabisan sumber daya. Karena setiap kebijakan merupakan contract, Anda dapat memperkuat perimeter tanpa melakukan fork pada engine, dan nama kebijakan diekspos untuk pencatatan audit. Perlakukan semua HTML, semua URL, serta semua byte fon dan gambar sebagai berbahaya. Halaman ini ditandai export_control_class: legal-review-required karena contract mengatur kebijakan kriptografi; teksnya memparafrasekan semua sumber normatif dan tidak mengutip satu pun.

KlaimStandarKlausaBukti
Penanganan URL yang tidak dibatasi memungkinkan perubahan URL melewati kontrol akses; kebijakan sumber daya eksternal memitigasinya melalui pengaturan bawaan yang menolak semua dan allowlist domain dengan pencocokan persis.OWASP Top 10 2025A01
Pengadaan komponen eksternal harus dibatasi pada sumber resmi dan transport yang aman; kebijakan ini mendukungnya melalui allowlist skema.OWASP Top 10 2025Rantai pasok perangkat lunak

Kedua poin tersebut memparafrasekan panduan OWASP. Halaman ini merujuk materi OWASP berdasarkan klausa; engine tidak mereproduksi teksnya.

Core mendefinisikan dan membekukan ketiga contract kebijakan tersebut. Core menyertakan pengaturan bawaan yang permisif untuk pengembangan serta pengaturan bawaan yang ketat untuk kebijakan sumber daya yang menolak semua. Edisi Enterprise menyertakan profil FIPS 140-3 di balik CryptoPolicyInterface, sehingga deployment yang teregulasi memperoleh postur algoritme yang tervalidasi tanpa mengubah kode penandatanganan atau enkripsi. Permukaan contract identik di seluruh edisi. Perbedaannya terletak pada implementasi kebijakan yang Anda suntikkan.