Przejdź do głównej zawartości

Contracts / Zasady bezpieczeństwa

Domena security-policy definiuje trzy kontrakty działające na zasadzie domyślnej odmowy: CryptoPolicyInterface kontroluje wybór algorytmów i kluczy, HtmlSecurityPolicyInterface ogranicza zakres funkcji Hypertext Markup Language (HTML), a ExternalResourcePolicyInterface zarządza ładowaniem zasobów zdalnych. Ponieważ każdy z nich jest kontraktem, można dostarczyć bardziej restrykcyjne zasady dla wdrożenia bez tworzenia forka.

Okno terminala
composer require nextpdf/core:^3

CryptoPolicyInterface jest bramą kryptograficzną. Moduł Core wywołuje ją przed każdym krokiem podpisywania, szyfrowania lub obliczania skrótu. Sprawdzenie obejmuje skrót, identyfikator obiektu podpisu (OID), szyfr oraz siłę klucza. Kontrakt zgłasza także minimalny skrót i nazwę zasad na potrzeby dziennika audytu. Użyj go, aby zastosować zestaw reguł, taki jak Federal Information Processing Standards (FIPS) 140-3 lub eIDAS. Kod podpisywania i szyfrowania pozostaje bez zmian. Gdy nie ustawiono żadnych zasad, dozwolony jest każdy algorytm. Witryna podlegająca regulacjom musi ustawić jawne zasady.

HtmlSecurityPolicyInterface działa na warstwie analizy składni HTML, zanim treść dotrze do jakiegokolwiek mechanizmu renderowania. Decyduje, czy znacznik, atrybut, właściwość Cascading Style Sheets (CSS) albo schemat adresu uniform resource locator (URL) jest dozwolony. Ogranicza także rozmiar danych wejściowych i głębokość zagnieżdżenia. Współpracuje z zasadami transportu właściwymi dla poszczególnych mechanizmów renderowania (Chrome, Cloudflare, Gotenberg), które ustawiają limity rozmiaru oraz nagłówki Content Security Policy (CSP). Zasady HTML zmniejszają powierzchnię ataku na warstwie analizy składni. Usunięty znacznik nigdy nie dociera do układu. Wstrzyknięty element nie może zmienić danych wyjściowych. Gdy nie ustawiono żadnych zasad, ustawienie domyślne dopuszcza pełny zestaw funkcji.

ExternalResourcePolicyInterface decyduje, czy potok HTML może pobrać zewnętrzną czcionkę, arkusz stylów lub obraz. Ustawia również limity dla każdego pobrania. Jego domyślną postawą jest odmowa wszystkiego. Każda opcja pozostaje wyłączona, dopóki jej nie włączysz. Kontrakt opiera się na zasadzie minimalnych uprawnień. Niezaufany kod HTML może wskazywać adres URL kontrolowany przez atakującego. Kontroluje pobrania @font-face według schematu, rozmiaru i liczby glifów. Kontroluje @import według schematu, głębokości i łącznego rozmiaru. Kontroluje background-image według listy schematów oraz listy dozwolonych domen z dopasowaniem dokładnym. Ogranicza rozmiar identyfikatorów data-URI (Uniform Resource Identifier). Kontroluje także zewnętrzne odwołania Scalable Vector Graphics (SVG). Kontrakt stanowi, że w środowisku produkcyjnym należy je zawsze odrzucać. Umożliwiają fałszowanie żądań oraz wstrzykiwanie skryptów. Nieograniczone pobieranie adresów URL tworzy ścieżkę fałszowania żądań po stronie serwera. Kontrolę dostępu można obejść przez zmianę adresu URL, jak opisano w Open Worldwide Application Security Project (OWASP) Top 10 2025. Pozyskiwanie komponentów zewnętrznych musi być ograniczone do oficjalnych źródeł i bezpiecznego transportu.

TypRodzajKluczowe składoweStabilnośćOd wersji
CryptoPolicyInterfaceinterfejsisHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName()stabilny1.9.0
HtmlSecurityPolicyInterfaceinterfejsisTagAllowed(), isAttributeAllowed(), isCssPropertyAllowed(), isUrlSchemeAllowed(), getMaxInputSize(), getMaxNestingDepth(), getName()stabilny3.1.0
ExternalResourcePolicyInterfaceinterfejsisFontFaceAllowed(), getAllowedFontSchemes(), getMaxFontFileSize(), getMaxFontGlyphs(), isImportAllowed(), getMaxImportDepth(), isBackgroundImageAllowed(), getAllowedImageDomains(), getMaxDataUrlSize(), isSvgExternalReferenceAllowed()stabilny4.0.0

ExternalResourcePolicyInterface zwraca limity z typami: rozmiary positive-int, głębokość importu int<1, 100> oraz listy schematów i domen list<non-empty-string>. Domyślna implementacja odmawia każdej funkcji.

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);
}

Funkcja zależy wyłącznie od kontraktu. Spełniają go zarówno zasady restrykcyjne, jak i domyślne.

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.');
}
}
}

Brama egzekwuje limit danych wejściowych i odrzuca niebezpieczne zasady dotyczące zasobów, zanim potok zostanie uruchomiony. Rejestruje nazwę zasad na potrzeby audytu i zgłasza konkretny wyjątek.

  • CryptoPolicyInterface dopuszcza każdy algorytm, gdy nie ustawiono żadnych zasad. Otwarte ustawienie domyślne służy wygodzie podczas programowania, a nie jako konfiguracja produkcyjna. W każdym wdrożeniu podlegającym regulacjom ustaw jawne zasady.
  • HtmlSecurityPolicyInterface::getMaxInputSize() zwraca 0 dla braku ograniczenia. Traktuj 0 jako „brak limitu zasad”, a nie „odrzuć wszystko”. Zastosuj również limit na warstwie transportowej.
  • ExternalResourcePolicyInterface domyślnie odmawia wszystkiego. Włączenie @font-face lub background-image bez ustawienia listy schematów otwiera powierzchnię fałszowania żądań; ustaw listę dozwolonych elementów jednocześnie.
  • Pusta lista dozwolonych domen w getAllowedImageDomains() oznacza, że po włączeniu obrazów tła dozwolone są wszystkie domeny. Pusta lista nie jest regułą odmowy; podaj jawne domeny.
  • isSvgExternalReferenceAllowed() powinno zwracać false w środowisku produkcyjnym. Kontrakt dokumentuje to wymaganie; zasady, które zwracają true, stanowią błąd, a nie wybór konfiguracji.

Sprawdzenie zasad to wywołanie predykatu: O(1), bez kosztu proporcjonalnego do danych wejściowych. Podczas analizy składni zasady są sprawdzane dla każdego znacznika, każdego atrybutu, każdej właściwości CSS i każdego adresu URL. Patologiczny dokument zwielokrotnia liczbę wywołań, ale każde wywołanie pozostaje w czasie stałym. Budżet performance_budget wynoszący 1500 ms czasu rzeczywistego i 64 MB szczytowego zużycia jest zdominowany przez analizę składni i renderowanie, a nie przez ocenę zasad. Limity rozmiaru danych wejściowych i głębokości zagnieżdżenia ograniczają własny koszt analizatora składni. Restrykcyjne zasady poprawiają wydajność w najgorszym przypadku, odrzucając zbyt duży lub głęboko zagnieżdżony dokument przed ułożeniem.

Te kontrakty tworzą perymetr obronny silnika, dlatego model zagrożeń jest jawny. CryptoPolicyInterface ogranicza obniżanie poziomu algorytmu, blokując słabe skróty i krótkie klucze przed jakąkolwiek operacją. HtmlSecurityPolicyInterface ogranicza ataki cross-site scripting wpływające na wyjście Portable Document Format (PDF) oraz wstrzykiwanie treści, usuwając niedozwolone znaczniki, atrybuty i kod CSS na warstwie analizy składni, zanim uruchomi się mechanizm renderowania. ExternalResourcePolicyInterface ogranicza fałszowanie żądań po stronie serwera, bomby dekompresyjne oraz bomby skumulowanego rozmiaru, domyślnie odmawiając wszystkiego i ograniczając każde pobranie według schematu, rozmiaru, głębokości i domeny. Limity rozmiaru danych wejściowych, głębokości zagnieżdżenia, liczby glifów czcionki i głębokości importu ograniczają wyczerpanie zasobów. Ponieważ każde zasady są kontraktem, można wzmocnić perymetr bez tworzenia forka silnika, a nazwa zasad jest udostępniana na potrzeby rejestrowania audytu. Traktuj cały kod HTML, wszystkie adresy URL oraz wszystkie bajty czcionek i obrazów jako wrogie. Ta strona jest oznaczona jako export_control_class: legal-review-required, ponieważ kontrakty regulują zasady kryptograficzne; treść parafrazuje wszystkie źródła normatywne i żadnego z nich nie cytuje.

TwierdzenieNormaKlauzulaDowody
Nieograniczona obsługa adresów URL pozwala obejść kontrolę dostępu przez zmianę adresu URL, co zasady dotyczące zasobów zewnętrznych ograniczają dzięki domyślnej odmowie wszystkiego oraz liście dozwolonych domen z dopasowaniem dokładnym.OWASP Top 10 2025A01
Pozyskiwanie komponentów zewnętrznych musi być ograniczone do oficjalnych źródeł i bezpiecznego transportu; zasady wspierają to za pomocą list dozwolonych schematów.OWASP Top 10 2025Łańcuch dostaw oprogramowania

Oba punkty parafrazują wytyczne OWASP. Strona odwołuje się do materiałów OWASP według klauzul; silnik nie odtwarza ich tekstu.

Moduł Core definiuje i zamraża trzy kontrakty zasad. Dostarcza liberalne ustawienia domyślne na potrzeby programowania oraz restrykcyjne ustawienia domyślne dla zasad zasobów odmawiających wszystkiego. Edycja Enterprise dostarcza profil FIPS 140-3 za interfejsem CryptoPolicyInterface, dzięki czemu wdrożenie podlegające regulacjom uzyskuje zatwierdzoną konfigurację algorytmów bez zmiany kodu podpisywania ani szyfrowania. Powierzchnia kontraktu jest identyczna we wszystkich edycjach. Różnicą jest implementacja zasad, którą wstrzykujesz.