Ga naar inhoud

Beveiliging en operationeel beheer voor Artisan

De bridge geeft mogelijk niet-vertrouwde HTML weer in Chrome, achter twee onafhankelijke netwerkbarrières en een strikt inhoudsbeleid. De besturingssysteem-sandbox van Chrome is een afzonderlijk, optioneel beheersmiddel met expliciete grenzen. Deze pagina documenteert die grens. Ze beweert niet dat die grens absoluut is.

Een weergave is server-side uitvoering van een verzoek: je toepassing geeft HTML door aan een browser-engine die standaard bronnen kan ophalen. Wanneer niet-vertrouwde invoer een uitgaande ophaalactie aanstuurt, ontstaat het risico op server-side request forgery (SSRF): CWE-918 in de Common Weakness Enumeration (CWE) definieert dit als een server die de inhoud van een opgegeven URL ophaalt zonder voldoende zekerheid dat het verzoek de verwachte bestemming bereikt. SSRF (CWE-918) is een zwakheid uit de CWE Top 25. De Application Security Verification Standard (ASVS) van het Open Worldwide Application Security Project (OWASP) vereist dat je uitgaande verzoeken vanuit servercomponenten beheert in plaats van ze impliciet toe te staan. De OWASP SSRF Prevention Cheat Sheet beschouwt het weigeren van oproepen naar willekeurige bestemmingen op netwerkniveau als het sterke beheersmiddel. Het deny-by-default-netwerkmodel hieronder is het antwoord van de bridge op die vereiste. Special Publication (SP) 800-53 SC-7 van het National Institute of Standards and Technology (NIST) beschrijft hetzelfde deny-all-permit-by-exception-grensprincipe dat de bridge op de transportlaag toepast.

HTML die aan de bridge wordt doorgegeven, wordt volledig in-process en binnen de lokale Chrome-instantie verwerkt. De bridge doet zelf geen enkele uitgaande netwerkoproep en blokkeert dat Chrome er een doet (zie het netwerkmodel hieronder), zodat invoerinhoud de host niet via de renderer verlaat. Persoonlijk identificeerbare informatie (PII) in de invoer komt terecht in de Portable Document Format (PDF)-uitvoer die je produceert, dus behandel de uitvoer met dezelfde residentiebeheersmiddelen als de invoer. De bridge slaat invoer of uitvoer niet op schijf op; persistentie is de verantwoordelijkheid van de aanroeper.

ChromeHtmlRenderer en BrowserPool accepteren een optionele PHP Standard Recommendation (PSR)-3 LoggerInterface. De bridge logt alleen operationele metadata: bytelengte van de invoer, doelbreedte en -hoogte, bytelengte van de uitvoer, gemeten inhoudshoogte, browserstart met het geconfigureerde binaire pad, herstartmeldingen met een weergavetelling en afsluitgebeurtenissen. De bridge logt geen HTML-inhoud, weergegeven bytes of geëxtraheerde tekst. Dit sluit aan bij de richtlijn van NIST SP 800-92 om operationele gebeurtenissen te loggen terwijl gevoelige payloads buiten de logs blijven. Het binaire pad wordt gelogd. Behandel het als niet-gevoelige implementatiemetadata. Tests bevestigen de vorm van de logaanroepen in tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize en tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.

De bridge past twee onafhankelijke barrières toe, zodat omzeiling van één barrière de host niet blootstelt:

  1. Content-Security-Policy. Elke weergave wordt door ChromeSecurityPolicy::wrapHtml() ingepakt in een document dat het volgende bevat:

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

    De Content Security Policy (CSP)-richtlijn default-src 'none' weigert alle bronoorsprongen. img-src data: staat alleen inline afbeeldingen toe. navigate-to 'none' blokkeert client-side navigatie. style-src 'unsafe-inline' is de enige versoepeling die nodig is om Chrome printToPDF inline-stijlen te laten toepassen. Geverifieerd in src/Artisan/ChromeSecurityPolicy.php en bevestigd door ChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives.

  2. Chrome DevTools Protocol (CDP)-transportblokkering. Voordat inhoud wordt geladen, verstuurt ChromeHtmlRenderer Network.enable en daarna Network.setBlockedURLs met het patroon ['*']. Dit blokkeert elke subresource-URL op de transportlaag van het Chrome DevTools Protocol, onafhankelijk van CSP. Geverifieerd in src/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests() en bevestigd door ChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests (die de exacte volgorde van de CDP-methoden en de parameter ['urls' => ['*']] controleert). Dit is de blokkering op netwerkniveau die de OWASP SSRF-richtlijn aanbeveelt als sterkste beheersmiddel, en het is een deny-all op transportniveau die consistent is met NIST SP 800-53 SC-7.

Het resultaat: een externe <img>, stylesheet, lettertype, script of iframe-URL in de invoer wordt niet geladen. De bridge implementeert geen domein-allowlist of privé-IP-filter omdat die niet nodig zijn: de bridge staat helemaal geen uitgaande subresource-ophaalactie toe.

Opmerking over drift: de nextpdf/core-docblock op writeHtmlChrome() zegt dat Chrome “externe bronnen zal ophalen” en adviseert een beleid te configureren om “privé-IP-bereiken te blokkeren en toegestane domeinen te beperken.” Dat beschrijft een configureerbaar allowlist-model. De meegeleverde Artisan ChromeSecurityPolicy biedt geen allowlist; deze blokkeert alle subresource-verzoeken onvoorwaardelijk. De code, niet de core-docblock, is gezaghebbend. Deze drift is vastgelegd voor het core-docsteam.

ChromeSecurityPolicy::validate() wordt uitgevoerd voordat de bridge contact opneemt met Chrome, en weigert:

ControleLimietOnderbouwing
HTML-grootte> maxHtmlSize (standaard 5 MB)Begrenzing tegen uitputting van bronnen (CWE Top 25 ongecontroleerd verbruik van bronnen)
Base64 data-URIcapture-groep >= 13_000_000 bytesBegrenzing tegen decompressiebommen
<meta http-equiv="refresh">elke (hoofdletterongevoelig, single/double aanhalingsteken)Blokkeert client-side omleidingen naar interne endpoints, een SSRF-navigatievector

Het blokkeren van meta-refresh is expliciete SSRF-versterking. Zonder deze blokkering zou door een aanvaller beheerde HTML Chrome kunnen omleiden naar een cloud-metadata-endpoint vóór printToPDF. Grensgedrag wordt bevestigd in ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).

Daarnaast verwijdert ChromeSecurityPolicy::wrapHtml() </style> uit defaultCss vóór injectie om te voorkomen dat een stijlblok uitbreekt naar de scriptcontext (bevestigd door ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).

De besturingssysteem-sandbox van Chrome is een afzonderlijk beheersmiddel naast de netwerkbarrières hierboven, en de bridge garandeert die sandbox niet.

  • Standaard is noSandbox false, dus Chrome start met zijn eigen sandbox ingeschakeld. De bridge implementeert die sandbox niet; de bridge vertrouwt op de sandbox van de Chrome-binary, die afhankelijk is van ondersteuning door de host-kernel.
  • Het instellen van noSandbox: true start Chrome met --no-sandbox. Dit verwijdert de procesisolatie-sandbox van Chrome. De optie is bedoeld voor containers waarin de sandbox niet kan initialiseren. Het is een reële vermindering van de isolatie: een gecompromitteerde renderer wordt niet langer ingeperkt door de sandbox van Chrome.
  • De netwerkbarrières van de bridge (CSP + CDP-blokkering) blijven van kracht, ongeacht of de sandbox is ingeschakeld, maar ze zijn geen vervanging voor procesisolatie. De OWASP ASVS-richtlijn voor minimale rechten is van toepassing: voer Chrome uit als een niet-root-gebruiker, in een beperkte container, met noSandbox alleen waar dit onvermijdelijk is, en behandel een --no-sandbox-implementatie als een implementatie die meer vertrouwen in de invoer vereist.

Deze documentatie beweert niet dat de bridge “standaard veilig” of “manipulatiebestendig” is. Ook beweert ze niet dat het uitschakelen van de sandbox veilig is. Ze vermeldt welke beheersmiddelen bestaan en waar hun grens ligt. Het inrichten van een sandbox-geschikte container wordt behandeld op de pagina /integrations/artisan/chrome-renderer-setup/.

Deze foutmodi zijn opgesomd op basis van src/Artisan/Exception/ en de render-/transportcode:

VoorwaardeVerschijnt alsBron
chrome-php/chrome-bibliotheek ontbreektChromeNotAvailableException (met installatieopdracht)BrowserPool::getBrowser()
HTML overschrijdt maxHtmlSizeRuntimeException (“exceeds maximum allowed size”)ChromeSecurityPolicy::validate()
Te grote base64 data-URIRuntimeException (“oversized base64 data URI”)ChromeSecurityPolicy::validate()
Verboden meta-refreshRuntimeException (“forbidden meta refresh redirect”)ChromeSecurityPolicy::validate()
Chrome-start / time-out / crashChromeRenderException (die de oorzaak omhult)ChromeHtmlRenderer::render()
Chrome retourneerde een lege PDFChromeRenderException (“returned empty data”)ChromeHtmlRenderer::render()
Pagina heeft geen content-streamPdfParseExceptionPageImporter::import()

Als ChromeRenderException tijdens de weergave wordt opgeworpen, wordt deze ongewijzigd opnieuw opgeworpen. Elke andere Throwable wordt omhuld als ChromeRenderException, met behoud van de vorige uitzondering (bevestigd door ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping en ::renderWrapsUnexpectedThrowablesWithChromeRenderException). De Chrome-pagina wordt altijd gesloten in een finally-blok, ook bij een fout.

  • Invoergrootte: maxHtmlSize (standaard 5 MB) en de base64-data-URI-limiet van 13 MB.
  • Tijd: renderTimeout seconden begrenst zowel het laden van inhoud als de synchrone CDP-oproepen. CDP-besturingsopdrachten gebruiken een vaste time-out van 5 seconden.
  • Proces: BrowserPool herstart Chrome om de 100 weergaven om geheugengroei te begrenzen en sluit het proces bij close() / destructie.

Dit zijn grenzen, geen quota. Gebruik voor elk pad dat aan niet-vertrouwde invoer wordt blootgesteld nog steeds een bronlimiet op hostniveau (cgroup, ulimit, verzoekbudget), in lijn met de CWE Top 25-richtlijn voor verbruik van bronnen.

Injecteer een PSR-3-logger om het begin van de weergave (grootte, breedte, hoogte), de voltooiing van de weergave (uitvoergrootte, inhoudshoogte), de browserstart (binair pad), de browserherstart (weergavetelling) en het sluiten van de browser (weergavetelling) vast te leggen. Dit zijn de enige gebeurtenissen die worden uitgezonden, en ze bevatten geen payload-inhoud. Gebruik ze voor service-level objectives (SLO’s) voor latentie en waarschuwingen over herstartfrequentie.

BeweringReferentieclause_idreference_id
Uitgaande verzoeken vanuit servercomponenten moeten worden beheerdOWASP ASVS 5.0§ (SSRF/uitgaande controle)
SSRF = server haalt een opgegeven URL op zonder de bestemming te validerenCWE Top 25 2025 (CWE-918)cwe_top25_2025#x28.x2.p2
SSRF (CWE-918) is een zwakheid uit de CWE Top 25CWE Top 25 2025cwe_top25_2025#x1.p73
Ongecontroleerd verbruik van bronnen is een zwakheid uit de CWE Top 25CWE Top 25 2025 (CWE-400)cwe_top25_2025#x19.x2.p2
Deny-by-default-grensbescherming (toestaan bij uitzondering)NIST SP 800-53 Rev 5 SC-7SC-7
Weigering op netwerkniveau van oproepen naar willekeurige bestemmingen is het sterke SSRF-beheersmiddelOWASP Cheat Sheet Series (SSRF Prevention §Network layer)owasp_cheatsheet_series#x132.x2
Bescherm URL-ophalende componenten tegen SSRFOWASP Cheat Sheet Series§ (SSRF-preventie, URL-ophaaltools)
Isoleer weergave van niet-vertrouwde inhoud, minimale rechtenOWASP ASVS 5.0§ (sandbox / minimale rechten)
Log operationele gebeurtenissen; houd payloads buiten de logsNIST SP 800-92§ (richtlijn voor loginhoud)

Citaten zijn opgehaald via de NextPDF compliance-engine (corpusmanifest 1d05b7c4…d790b6); clausuletekst is geparafraseerd, nooit geciteerd.

DreigingBeheersmiddelRestrisico
SSRF via externe subresourceCSP default-src 'none' + CDP setBlockedURLs('*')Een Chrome-engine-bug die beide barrières omzeilt (defense in depth verlaagt het risico, maar elimineert het niet)
SSRF via meta-refresh-navigatiePre-Chrome-validatie weigert de tagEen nieuwe navigatievector die niet door het patroon wordt herkend
Uitputting van bronnenInvoergrootte + base64-limieten + time-out + herstart na 100 weergavenGeen quotum per host; combineer met cgroup/ulimit
Compromittering van het rendererprocesChrome-sandbox wanneer ingeschakeldnoSandbox: true verwijdert dit beheersmiddel volledig
Stijluitbraak / injectie</style>-verwijdering in defaultCss; CSP blokkeert scriptInjectie via een toekomstige vector die niet wordt verwijderd

De bridge voert geen cryptografische bewerkingen uit. Ze produceert PDF-bytes via Chrome en sluit deze in. Ondertekening, versleuteling en het gedrag in de Federal Information Processing Standards (FIPS)-modus zijn aangelegenheden van core/Premium en worden niet beïnvloed door Artisan.

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