Bezpieczeństwo i operacje
W skrócie
Dział zatytułowany „W skrócie”Ten most przesyła kod Hypertext Markup Language (HTML) przez granicę sieciową do silnika przeglądarki. Na tej stronie udokumentowano mechanizmy kontroli chroniące tę granicę, traktując kod źródłowy jako źródło prawdy. Gdy mechanizm kontroli powołuje się na standard, cytowany jest standard zadeklarowany w docblocku kodu. Strona powtarza twierdzenie zawarte w kodzie; nie odtwarza tekstu normatywnego.
Model zagrożeń
Dział zatytułowany „Model zagrożeń”Docblocki pakietu wymieniają zagrożenia, przed którymi pakiet się broni:
- XSS-do-PDF — cross-site scripting (XSS) za pośrednictwem złośliwego znacznika, który wykonuje się podczas renderowania formatu Portable Document Format (PDF).
- SSRF — server-side request forgery (SSRF) spowodowane znacznikiem lub docelowym adresem Uniform Resource Locator (URL), prowadzące do wysłania żądania na adres wewnętrzny.
- Wyczerpanie zasobów — nadmiernie duże dane wejściowe lub bomba dekompresyjna.
- DNS rebinding — atak DNS rebinding (Domain Name System), w którym nazwa hosta przechodzi walidację, a następnie w chwili połączenia zostaje rozwiązana na adres prywatny.
- Przechwytywanie TLS na ścieżce — przechwytywanie Transport Layer Security (TLS) na ścieżce do Workera z użyciem podstawionego certyfikatu.
Dla każdego zagrożenia poniżej wskazano konkretny, testowalny mechanizm kontroli.
Kontrola danych wejściowych (zanim żądanie opuści PHP)
Dział zatytułowany „Kontrola danych wejściowych (zanim żądanie opuści PHP)”CloudflareSecurityPolicy::validate() jest wykonywane przed zbudowaniem jakiegokolwiek żądania:
| Mechanizm kontroli | Zachowanie | Źródło limitu |
|---|---|---|
| Limit rozmiaru | Odrzuca kod HTML większy niż maxHtmlSize | CloudflareRendererConfig, domyślnie 5000000 bajtów |
| Zabezpieczenie przed bombą dekompresyjną Base64 | Szacuje rozmiar po zdekodowaniu każdego identyfikatora URI data:…;base64,…; odrzuca wartości równe pułapowi lub większe | MAX_DATA_URI_BYTES = 13631488 |
| Zakaz meta-refresh | Odrzuca każdy <meta http-equiv="refresh">, bez rozróżniania wielkości liter | wyrażenie regularne w CloudflareSecurityPolicy |
Naruszenie powoduje zgłoszenie RuntimeException z komunikatem zawierającym naruszającą wartość oraz limit. Zakaz meta-refresh wprowadzono, ponieważ dyrektywa odświeżania może rozpocząć nawigację wewnątrz strony renderowanej przez Workera — to wektor SSRF osadzony w treści, a nie w adresie URL.
Polityka bezpieczeństwa HTML z nextpdf/core (HtmlSecurityPolicyInterface, domyślnie DefaultHtmlSecurityPolicy) działa w warstwie parsowania i uzupełnia powyższe kontrole w warstwie transportu. Uzyskaj ją za pomocą getHtmlSecurityPolicy(). Wstrzyknij własną za pośrednictwem konstruktora.
Kontrola miejsca docelowego (SSRF i DNS rebinding)
Dział zatytułowany „Kontrola miejsca docelowego (SSRF i DNS rebinding)”CloudflareSecurityPolicy::validateWorkerUrl():
- Odrzuca adres URL, którego nie da się sparsować lub który nie zawiera części scheme/host (
Invalid Worker URL). - Odrzuca każdy schemat inny niż HTTPS (
Worker URL must use HTTPS). - W przypadku hosta będącego literałem IP odrzuca zakresy prywatne lub zarezerwowane za pomocą
flag PHP
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE. W praktyce oznacza to odrzucanie przestrzeni prywatnej RFC 1918, adresów pętli zwrotnej oraz adresów link-local RFC 3927. Testy wprost obejmują odrzucanie adresów192.168.x,127.0.0.1oraz169.254.x. O przynależności do zakresu decyduje rozszerzenie filter w PHP; pakiet nie wiąże tej decyzji z konkretną klauzulą. RFC 1918 oraz RFC 3927 wymieniono tu opisowo jako powszechnie znane definicje tych zakresów. - W przypadku nazwy hosta rozwiązuje wszystkie rekordy A i AAAA za pomocą
dns_get_record()(a niegethostbyname(), które zwraca tylko pierwszą odpowiedź) i odrzuca hosta, jeśli którykolwiek z rozwiązanych adresów jest prywatny lub zarezerwowany.
Rozwiązywanie wszystkich rekordów jest celowe. Docblock klasy opisuje je jako ochronę przed hostem, który zwraca kilka rekordów: wyszukiwanie pojedynczego rekordu mogłoby wybrać adres publiczny, podczas gdy późniejsze połączenie wybrałoby prywatny. Jest to zgodne z OWASP SSRF Prevention Cheat Sheet: dla domeny należy rozwiązać zarówno rekordy A, jak i AAAA oraz zastosować kontrolę adresów niepublicznych do całego zbioru wyników.
validateWorkerUrl() zwraca zweryfikowany zbiór adresów IP. Bezpośrednio przed wysłaniem żądania renderer wywołuje assertPinsStillValid(). To wywołanie ponownie rozwiązuje hosta i odrzuca nowo zaobserwowany adres IP (Worker URL DNS answer changed since validation — possible DNS rebinding attack). Zamyka to okno między czasem sprawdzenia a czasem użycia, czyli między walidacją a połączeniem.
Kontrola transportu (PinnedCurlTransport)
Dział zatytułowany „Kontrola transportu (PinnedCurlTransport)”Jeśli dostępny jest zweryfikowany zbiór adresów IP lub zbiór przypięć Subject Public Key Info (SPKI) oraz dostarczono fabrykę ResponseFactory zgodną z PHP Standards Recommendation 17 (PSR-17), renderer używa Transport\PinnedCurlTransport zamiast wstrzykniętego klienta PHP Standards Recommendation 18 (PSR-18). Transport egzekwuje te mechanizmy kontroli na poziomie uchwytu cURL:
- Przypięcie DNS —
CURLOPT_RESOLVEwiąże parę host:port ze zweryfikowanym zbiorem adresów IP, więc libcurl nie wykonuje własnego wyszukiwania w momencie nawiązywania połączenia. To powiązanie sprawia, że sprawdzenie DNS po stronie aplikacji dotyczy faktycznego połączenia; bez niego libcurl mógłby rozwiązać inny adres. - Przypinanie klucza publicznego TLS —
CURLOPT_PINNEDPUBLICKEYjest ustawiane ze scalonego zbioru przypięć. Jest to zgodne z RFC 7469 §2.6: przypięte połączenie jest akceptowane, gdy zbiór odcisków SPKI przedstawionych przez serwer ma część wspólną ze skonfigurowanym zbiorem przypięć, a niepowodzenie walidacji przypięcia jest nieodwracalne. Ciągi przypięć są normalizowane z postacisha256/<base64>do postaci wymaganej przez cURLsha256//<base64>; nieprawidłowe przypięcie powoduje zgłoszenieInvalidSpkiPinException. - Weryfikacja TLS włączona —
CURLOPT_SSL_VERIFYPEER => true,CURLOPT_SSL_VERIFYHOST => 2. - Brak automatycznych przekierowań —
CURLOPT_FOLLOWLOCATION => false,CURLOPT_MAXREDIRS => 0. Odpowiedź 3xx jest przekazywana do warstwy polityki, zamiast być śledzona przez libcurl do niezweryfikowanego hosta. Docblock klasy wskazuje, że jest to celowe, więc przekierowania są ponownie walidowane, a nie śledzone po cichu. - Twardy limit czasu —
CURLOPT_TIMEOUTjest ustawiany zrenderTimeout(domyślnie30sekund).
Błąd cURL albo treść, która nie jest łańcuchem znaków, powoduje zgłoszenie CloudflareRenderException z numerem i komunikatem błędu cURL.
Wskazówki operacyjne dotyczące przypinania
Dział zatytułowany „Wskazówki operacyjne dotyczące przypinania”Konfiguracja zawiera pinnedPublicKeys oraz osobne backupPublicKeys. RFC 7469 §2.5 opisuje przypięcie zapasowe jako odcisk dla zapasowej, jeszcze niewdrożonej pary kluczy przechowywanej offline i traktuje je jako podstawową ścieżkę odzyskiwania przy niezamierzonym niepowodzeniu walidacji przypięcia. Zachowaj co najmniej jedno przypięcie zapasowe, aby rotacja certyfikatu nie unieruchomiła punktu końcowego. Osobne pole pozwala niezależnie zweryfikować rotację. Operacyjnie:
- Przypnij SPKI certyfikatu liścia lub certyfikatu pośredniego, którego rotację kontrolujesz.
- Przed rotacją zawsze skonfiguruj przypięcie zapasowe dla następnego certyfikatu.
- Pusty zbiór przypięć wyłącza przypinanie; stosuj to tylko ze stabilnym, znanym łańcuchem certyfikatów. Przypinanie jest opcjonalne i włączane konfiguracją.
Uwierzytelnianie i obsługa sekretów
Dział zatytułowany „Uwierzytelnianie i obsługa sekretów”- Żądanie do Workera zawiera
Authorization: Bearer <apiToken>.apiTokenjest oznaczony jako#[SensitiveParameter], więc ślady stosu go nie ujawniają. Sonda osiągalności wysyła ten sam nagłówek Bearer w żądaniu Hypertext Transfer Protocol (HTTP)HEAD. - Klucze dostępu Cloudflare R2 (
accessKeyId,secretAccessKey) są oznaczone jako#[SensitiveParameter]i służą wyłącznie do wyprowadzenia klucza podpisującego Amazon Web Services (AWS) Signature V4. ApiKeyValidatorporównuje klucze za pomocąhash_equals()w sposób bezpieczny pod względem czasowym i obsługuje przechowywanie kluczy w postaci skrótu Secure Hash Algorithm 256 (SHA-256) za pomocąvalidateHashed().- Obiekty konfiguracji są
final readonly— sekret ustawiony raz nie może zostać zmieniony. - Pobieraj sekrety ze zmiennych środowiskowych lub z menedżera sekretów. Nigdy nie zatwierdzaj ich w repozytorium. Pakiet stosuje szerszy standard bezpieczeństwa NextPDF: PHPStan Level 10,
declare(strict_types=1)w każdym pliku, brakeval()/exec(), GitHub Actions przypięte do SHA.
Czego ten pakiet nie twierdzi
Dział zatytułowany „Czego ten pakiet nie twierdzi”- Nie podaje żadnych limitów platformy Cloudflare (czasu procesora Workera, pamięci, limitu treści żądania ani liczby podżądań). Jedyne limity rozmiaru i czasu, jakie wskazuje ta dokumentacja, to te, które pakiet egzekwuje samodzielnie, wymienione powyżej oraz w /integrations/cloudflare/configuration/. W kwestii limitów platformy zapoznaj się z oficjalną dokumentacją Cloudflare oraz z własną implementacją Workera.
- Nie podpisuje plików PDF i nie formułuje żadnego twierdzenia o zgodności podpisu. Gdy podpisy są wymagane, renderuj tutaj, a następnie podpisz za pomocą silnika. NextPDF Pro zapewnia wyłącznie podpisywanie PDF Advanced Electronic Signatures (PAdES) B-B; profile długoterminowej walidacji są funkcją Enterprise i pozostają poza zakresem tego mostu.
- Nie certyfikuje ani nie gwarantuje potoku i nie czyni go „odpornym na manipulacje”. Implementuje wyłącznie konkretne mechanizmy kontroli, opisane na tej stronie i możliwe do zweryfikowania w kodzie.
Runbook operacyjny
Dział zatytułowany „Runbook operacyjny”| Objaw | Najpierw sprawdź |
|---|---|
Worker URL must use HTTPS | Sprawdź schemat skonfigurowanego workerUrl. |
private or reserved IP | Rekordy DNS nazwy hosta Workera; poszukaj rekordu, który rozwiązuje się do przestrzeni RFC 1918 / pętli zwrotnej / RFC 3927. |
DNS answer changed since validation | Niestabilność DNS lub próba rebindingu; ponownie rozwiąż nazwę i przejrzyj pełny zbiór rekordów. |
cURL transport error | Ścieżkę sieciową, łańcuch TLS oraz — jeśli ustawiono przypięcia — to, czy SPKI prezentowanego certyfikatu nadal znajduje się w zbiorze przypięć. |
| Renderowanie zawodzi tuż po rotacji certyfikatu | Zbiór przypięć bez pasującego przypięcia zapasowego. Dodaj nowe SPKI jako zapasowe przed rotacją. |
is not installed / no LocalRendererFactoryInterface | Fallback jest włączony, ale nie podłączono żadnej fabryki albo brakuje nextpdf/artisan. |
| Niespójne odrzucenia przez limiter szybkości między węzłami | Limiter w pamięci działa na poziomie procesu; umieść przed nim współdzielony magazyn. |
Zgłaszanie incydentów
Dział zatytułowany „Zgłaszanie incydentów”Zgłaszaj podatności przez GitHub Security Advisories albo kanał kontaktowy ds. bezpieczeństwa podany w pliku SECURITY.md repozytorium. Nie zgłaszaj problemów bezpieczeństwa jako publicznych zgłoszeń (issues) w serwisie GitHub.
Zobacz też
Dział zatytułowany „Zobacz też”- /integrations/cloudflare/overview/ — dlaczego pakiet jest zaprojektowany wokół granicy.
- /integrations/cloudflare/configuration/ — pola dotyczące zbioru przypięć i limitów.
- /integrations/cloudflare/troubleshooting/ — pełne mapowanie niepowodzeń na wyjątki.