Przejdź do głównej zawartości

Bezpieczeństwo i eksploatacja Artisan

Mostek renderuje w Chrome potencjalnie niezaufany kod HTML za dwiema niezależnymi barierami sieciowymi i restrykcyjną polityką treści. Piaskownica Chrome na poziomie systemu operacyjnego to odrębny, opcjonalny mechanizm o jasno określonych ograniczeniach. Ta strona dokumentuje tę granicę. Nie twierdzi, że jest ona absolutna.

Renderowanie oznacza wykonanie żądania po stronie serwera: aplikacja przekazuje kod HTML silnikowi przeglądarki, który domyślnie może pobierać zasoby. Gdy niezaufane dane wejściowe sterują pobieraniem zasobów zewnętrznych, ryzykiem jest fałszowanie żądań po stronie serwera (SSRF): wpis Common Weakness Enumeration (CWE) CWE-918 definiuje je jako pobranie przez serwer zawartości podanego adresu URL bez wystarczającej pewności, że żądanie dociera do oczekiwanego miejsca docelowego. SSRF (CWE-918) należy do słabości z listy CWE Top 25. Application Security Verification Standard (ASVS) organizacji Open Worldwide Application Security Project (OWASP) wymaga kontrolowania żądań wychodzących z komponentów serwerowych, zamiast zdawania się na ustawienia domyślne. OWASP SSRF Prevention Cheat Sheet uznaje blokowanie na warstwie sieciowej wywołań do dowolnych miejsc docelowych za silny mechanizm kontroli. Opisana poniżej konfiguracja sieciowa z domyślną odmową jest odpowiedzią mostka na to wymaganie. National Institute of Standards and Technology (NIST) Special Publication (SP) 800-53 SC-7 opisuje tę samą zasadę granicy: „odmów wszystkiego, zezwalaj na wyjątki”, którą mostek stosuje na warstwie transportowej.

Rezydencja danych i ograniczanie ryzyka związanego z danymi osobowymi

Dział zatytułowany „Rezydencja danych i ograniczanie ryzyka związanego z danymi osobowymi”

Kod HTML przekazany do mostka jest przetwarzany w całości w ramach procesu i lokalnej instancji Chrome. Mostek nie wykonuje żadnych własnych wychodzących żądań sieciowych i uniemożliwia Chrome wykonywanie takich żądań (zobacz model sieciowy poniżej), więc treść wejściowa nie opuszcza hosta przez mechanizm renderujący. Dane osobowe (PII) zawarte w danych wejściowych są renderowane do tworzonego wyjściowego pliku Portable Document Format (PDF), dlatego do danych wyjściowych stosuj takie same mechanizmy kontroli rezydencji jak do danych wejściowych. Mostek nie utrwala danych wejściowych ani wyjściowych na dysku; utrwalanie należy do obowiązków wywołującego.

ChromeHtmlRenderer i BrowserPool przyjmują opcjonalny LoggerInterface zgodny z PHP Standard Recommendation (PSR)-3. Mostek rejestruje wyłącznie metadane operacyjne: długość wejścia w bajtach, docelową szerokość i wysokość, długość wyjścia w bajtach, zmierzoną wysokość treści, uruchomienie przeglądarki ze skonfigurowaną ścieżką pliku binarnego, powiadomienia o ponownym uruchomieniu z liczbą renderowań oraz zdarzenia zamknięcia. Nie rejestruje treści HTML, renderowanych bajtów ani wyodrębnionego tekstu. Jest to zgodne z wytycznymi NIST SP 800-92: należy rejestrować zdarzenia operacyjne, ale nie zapisywać w dziennikach danych wrażliwych. Ścieżka pliku binarnego jest rejestrowana. Traktuj ją jako niewrażliwe metadane wdrożenia. Testy potwierdzają kształty wywołań rejestrujących w tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize oraz tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.

Mostek stosuje dwie niezależne bariery, tak aby obejście jednej z nich nie narażało hosta:

  1. Content-Security-Policy. Każde renderowanie jest opakowywane przez ChromeSecurityPolicy::wrapHtml() w dokument, który zawiera:

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

    Dyrektywa Content Security Policy (CSP) default-src 'none' odmawia dostępu do wszystkich źródeł zasobów. img-src data: zezwala wyłącznie na obrazy osadzone w treści. navigate-to 'none' blokuje nawigację po stronie klienta. style-src 'unsafe-inline' to jedyne złagodzenie wymagane, aby Chrome printToPDF mógł zastosować style osadzone w treści. Zweryfikowano to w src/Artisan/ChromeSecurityPolicy.php i potwierdzono przez ChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives.

  2. Blokada transportu Chrome DevTools Protocol (CDP). Przed załadowaniem treści ChromeHtmlRenderer wysyła Network.enable, a następnie Network.setBlockedURLs ze wzorcem ['*']. Blokuje to każdy adres URL podzasobu na warstwie transportowej Chrome DevTools Protocol, niezależnie od CSP. Zweryfikowano to w src/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests() i potwierdzono przez ChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests (który sprawdza dokładną kolejność metod CDP oraz parametr ['urls' => ['*']]). Jest to blokada na warstwie sieciowej, którą wytyczne OWASP SSRF zalecają jako najsilniejszy mechanizm kontroli, a zarazem domyślna odmowa na całej warstwie transportowej, zgodna z NIST SP 800-53 SC-7.

Skutek: zdalny adres URL w elemencie <img>, arkuszu stylów, czcionce, skrypcie lub ramce iframe w danych wejściowych nie zostaje załadowany. Mostek nie implementuje listy dozwolonych domen ani filtra prywatnych adresów IP, ponieważ ich nie potrzebuje: nie zezwala na żadne pobieranie podzasobów na zewnątrz.

Uwaga o rozbieżności: docblock nextpdf/core przy writeHtmlChrome() mówi, że Chrome „będzie pobierać zasoby zewnętrzne”, i zaleca skonfigurowanie polityki tak, aby „blokować prywatne zakresy IP i ograniczać dozwolone domeny.” Opisuje to konfigurowalny model listy dozwolonych. Dostarczana w Artisan polityka ChromeSecurityPolicy nie udostępnia listy dozwolonych; blokuje wszystkie żądania podzasobów bezwarunkowo. Rozstrzygający jest kod, a nie docblock w core. Ta rozbieżność została odnotowana dla zespołu dokumentacji core.

ChromeSecurityPolicy::validate() działa, zanim mostek skontaktuje się z Chrome, i odrzuca:

KontrolaLimitUzasadnienie
Rozmiar HTML> maxHtmlSize (domyślnie 5 MB)Ogranicza wyczerpywanie zasobów (niekontrolowane zużycie zasobów z listy CWE Top 25)
URI danych base64grupa przechwytująca >= 13_000_000 bajtówOgranicza ryzyko bomby dekompresyjnej
<meta http-equiv="refresh">dowolny (bez rozróżniania wielkości liter, pojedynczy/podwójny cudzysłów)Blokuje przekierowania po stronie klienta do wewnętrznych punktów końcowych — wektor nawigacyjny SSRF

Blokowanie meta-refresh to jawne wzmocnienie ochrony przed SSRF. Bez niego kontrolowany przez atakującego kod HTML mógłby przekierować Chrome do punktu końcowego metadanych chmury przed printToPDF. Warunki brzegowe potwierdza ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).

Dodatkowo ChromeSecurityPolicy::wrapHtml() usuwa </style> z defaultCss przed wstrzyknięciem, aby zapobiec wyjściu z bloku stylów do kontekstu skryptu (potwierdza to ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).

Piaskownica Chrome na poziomie systemu operacyjnego to mechanizm odrębny od opisanych powyżej barier sieciowych, a mostek go nie gwarantuje.

  • Domyślnie noSandbox ma wartość false, więc Chrome uruchamia się z włączoną własną piaskownicą. Mostek nie implementuje tej piaskownicy; polega na piaskownicy zapewnianej przez plik binarny Chrome, która zależy od obsługi w jądrze hosta.
  • Ustawienie noSandbox: true uruchamia Chrome z --no-sandbox. Usuwa to piaskownicę izolacji procesów w Chrome. Jest udostępniane dla kontenerów, w których piaskownica nie może się zainicjować. To realne zmniejszenie izolacji: skompromitowanie mechanizmu renderującego nie jest już ograniczane przez piaskownicę Chrome.
  • Bariery sieciowe mostka (CSP + blokada CDP) pozostają w mocy niezależnie od tego, czy piaskownica jest włączona, ale nie zastępują izolacji procesów. Stosują się wytyczne OWASP ASVS dotyczące najmniejszych uprawnień: uruchamiaj Chrome jako użytkownik bez uprawnień roota, w ograniczonym kontenerze, z noSandbox tylko tam, gdzie jest to nieuniknione, i traktuj wdrożenie z --no-sandbox jako wymagające wyższego poziomu zaufania do danych wejściowych.

Ta dokumentacja nie twierdzi, że mostek jest „bezpieczny domyślnie” ani „odporny na manipulacje”. Nie twierdzi też, że wyłączenie piaskownicy jest bezpieczne. Określa istniejące mechanizmy kontroli oraz granice ich działania. Przygotowanie kontenera obsługującego piaskownicę zostało omówione na stronie /integrations/artisan/chrome-renderer-setup/.

Te tryby awarii wynikają z src/Artisan/Exception/ oraz kodu renderowania/transportu:

WarunekZgłaszany jakoŹródło
Brak biblioteki chrome-php/chromeChromeNotAvailableException (z poleceniem instalacji)BrowserPool::getBrowser()
HTML przekracza maxHtmlSizeRuntimeException („exceeds maximum allowed size”)ChromeSecurityPolicy::validate()
Zbyt duży URI danych base64RuntimeException („oversized base64 data URI”)ChromeSecurityPolicy::validate()
Niedozwolony meta-refreshRuntimeException („forbidden meta refresh redirect”)ChromeSecurityPolicy::validate()
Uruchomienie / przekroczenie czasu / awaria ChromeChromeRenderException (opakowujący przyczynę)ChromeHtmlRenderer::render()
Chrome zwrócił pusty plik PDFChromeRenderException („returned empty data”)ChromeHtmlRenderer::render()
Strona nie ma strumienia treściPdfParseExceptionPageImporter::import()

Jeśli ChromeRenderException zostanie zgłoszony w trakcie renderowania, jest ponownie rzucany bez zmian. Każdy inny Throwable jest opakowywany jako ChromeRenderException, z zachowaniem poprzedniego wyjątku (potwierdzają to ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping oraz ::renderWrapsUnexpectedThrowablesWithChromeRenderException). Strona Chrome jest zawsze zamykana w bloku finally, nawet w razie awarii.

  • Rozmiar wejścia: maxHtmlSize (domyślnie 5 MB) oraz limit 13 MB dla URI danych base64.
  • Czas: renderTimeout sekund ogranicza zarówno ładowanie treści, jak i synchroniczne wywołania CDP. Polecenia sterujące CDP używają stałego limitu czasu wynoszącego 5 sekund.
  • Proces: BrowserPool ponownie uruchamia Chrome co 100 renderowań, aby ograniczyć wzrost zużycia pamięci, i zamyka proces przy close() / zniszczeniu.

Są to ograniczenia, a nie kwoty zasobów. W przypadku każdej ścieżki narażonej na niezaufane dane wejściowe nadal stosuj limity zasobów na poziomie hosta (cgroup, ulimit, budżet żądań), zgodnie z wytycznymi CWE Top 25 dotyczącymi zużycia zasobów.

Wstrzyknij rejestrator zgodny z PSR-3, aby przechwytywać rozpoczęcie renderowania (rozmiar, szerokość, wysokość), zakończenie renderowania (rozmiar wyjścia, wysokość treści), uruchomienie przeglądarki (ścieżka pliku binarnego), ponowne uruchomienie przeglądarki (liczba renderowań) oraz zamknięcie przeglądarki (liczba renderowań). To jedyne emitowane zdarzenia i nie zawierają żadnej treści ładunku. Wykorzystuj je do SLO dotyczących opóźnień oraz do powiadamiania o częstości ponownych uruchomień.

StwierdzenieOdwołanieclause_idreference_id
Żądania wychodzące z komponentów serwerowych muszą być kontrolowaneOWASP ASVS 5.0§ (SSRF/kontrola żądań wychodzących)
SSRF = serwer pobiera podany adres URL bez walidacji miejsca docelowegoCWE Top 25 2025 (CWE-918)cwe_top25_2025#x28.x2.p2
SSRF (CWE-918) należy do słabości z listy CWE Top 25CWE Top 25 2025cwe_top25_2025#x1.p73
Niekontrolowane zużycie zasobów należy do słabości z listy CWE Top 25CWE Top 25 2025 (CWE-400)cwe_top25_2025#x19.x2.p2
Ochrona granicy z domyślną odmową (zezwalanie na wyjątek)NIST SP 800-53 Rev 5 SC-7SC-7
Blokada na warstwie sieciowej wywołań do dowolnych miejsc docelowych to silny mechanizm kontroli SSRFOWASP Cheat Sheet Series (SSRF Prevention §Network layer)owasp_cheatsheet_series#x132.x2
Chroń przed SSRF komponenty pobierające adresy URLOWASP Cheat Sheet Series§ (zapobieganie SSRF, narzędzia pobierające adresy URL)
Izoluj renderowanie niezaufanych treści, najmniejsze uprawnieniaOWASP ASVS 5.0§ (piaskownica / najmniejsze uprawnienia)
Rejestruj zdarzenia operacyjne; nie umieszczaj ładunków w dziennikachNIST SP 800-92§ (wytyczne dotyczące treści dziennika)

Cytaty zostały pobrane za pomocą silnika zgodności NextPDF (manifest korpusu 1d05b7c4…d790b6); tekst klauzul jest parafrazowany i nigdy nie jest cytowany.

ZagrożenieMechanizm kontroliRyzyko szczątkowe
SSRF przez zdalny podzasóbCSP default-src 'none' + CDP setBlockedURLs('*')Błąd silnika Chrome, który obchodzi obie bariery (obrona w głąb obniża ryzyko, ale go nie eliminuje)
SSRF przez nawigację meta-refreshWalidacja przed Chrome odrzuca ten znacznikNowy wektor nawigacyjny nieobjęty wzorcem
Wyczerpanie zasobówLimity rozmiaru wejścia i base64 + limit czasu + ponowne uruchomienie co 100 renderowańBrak kwoty na poziomie hosta; połącz z cgroup/ulimit
Skompromitowanie procesu mechanizmu renderującegoPiaskownica Chrome, gdy jest włączonanoSandbox: true całkowicie usuwa ten mechanizm kontroli
Wyjście z bloku stylów / wstrzyknięcieUsuwanie </style> w defaultCss; CSP blokuje skryptyWstrzyknięcie przez przyszły wektor, który nie jest usuwany

Mostek nie wykonuje żadnych operacji kryptograficznych. Produkuje bajty PDF za pośrednictwem Chrome i je osadza. Podpisywanie, szyfrowanie oraz zachowanie w trybie Federal Information Processing Standards (FIPS) należą do core/Premium i pozostają niezmienione przez Artisan.

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