Salta ai contenuti

Sicurezza e operazioni di Artisan

Il bridge esegue il rendering di HTML potenzialmente non attendibile in Chrome, protetto da due barriere di rete indipendenti e da una content policy rigorosa. La sandbox del sistema operativo di Chrome è un controllo separato e facoltativo, con limiti dichiarati. Questa pagina documenta tale limite; non afferma che sia assoluto.

Un rendering equivale all’esecuzione di una richiesta lato server: l’applicazione passa l’HTML a un motore browser che, per impostazione predefinita, può recuperare risorse. Un recupero in uscita determinato da input non attendibile è una server-side request forgery: la CWE-918 la definisce come il caso in cui un server recupera il contenuto di un URL fornito senza garantire in modo sufficiente che la richiesta raggiunga la destinazione prevista. L’SSRF (CWE-918) rientra nella CWE Top 25. OWASP ASVS richiede che le richieste in uscita dai componenti server siano controllate, non implicite. L’OWASP SSRF Prevention Cheat Sheet considera il diniego a livello di rete delle chiamate verso destinazioni arbitrarie il controllo robusto. L’impostazione di rete con diniego predefinito descritta di seguito è la risposta del bridge a tale requisito. NIST SP 800-53 SC-7 descrive lo stesso principio di limite “deny-all, permit-by-exception” che il bridge applica al livello di trasporto.

Residenza dei dati e mitigazioni dei dati personali (PII)

Sezione intitolata “Residenza dei dati e mitigazioni dei dati personali (PII)”

L’HTML passato al bridge viene elaborato interamente in-process e nell’istanza locale di Chrome. Il bridge non effettua chiamate di rete in uscita proprie e impedisce a Chrome di effettuarle (vedere il modello di rete più avanti), per cui il contenuto in ingresso non lascia l’host attraverso il renderer. I dati personali (PII) presenti nell’input vengono inclusi nel PDF prodotto dal rendering: trattare l’output con gli stessi controlli di residenza applicati all’input. Il bridge non rende persistenti input o output su disco; la persistenza resta responsabilità del chiamante.

ChromeHtmlRenderer e BrowserPool accettano un’interfaccia PSR-3 LoggerInterface facoltativa. Il bridge registra solo metadati operativi: lunghezza in byte dell’input, larghezza e altezza di destinazione, lunghezza in byte dell’output, altezza misurata del contenuto, avvio del browser con il percorso del binario configurato, avvisi di riavvio con conteggio dei rendering ed eventi di chiusura. Non registra il contenuto HTML, i byte del rendering o il testo estratto. Questo è coerente con le indicazioni di NIST SP 800-92: registrare gli eventi operativi mantenendo i payload sensibili fuori dai log. Il percorso del binario viene registrato; trattarlo come metadato di distribuzione non sensibile. La forma delle chiamate di log è verificata da tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize e tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.

Modello di isolamento di rete (difesa in profondità)

Sezione intitolata “Modello di isolamento di rete (difesa in profondità)”

Il bridge applica due barriere indipendenti, così che l’elusione di una non esponga l’host:

  1. Content-Security-Policy. Ogni rendering viene racchiuso da ChromeSecurityPolicy::wrapHtml() in un documento che include:

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

    default-src 'none' nega tutte le origini delle risorse. img-src data: consente soltanto immagini inline. navigate-to 'none' blocca la navigazione lato client. style-src 'unsafe-inline' è l’unica eccezione necessaria affinché printToPDF di Chrome applichi gli stili inline. Verificato in src/Artisan/ChromeSecurityPolicy.php e attestato da ChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives.

  2. Blocco del trasporto CDP. Prima di caricare il contenuto, ChromeHtmlRenderer invia Network.enable quindi Network.setBlockedURLs con il pattern ['*'], bloccando ogni URL di sottorisorsa al livello di trasporto del Chrome DevTools Protocol, indipendentemente dalla CSP. Verificato in src/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests() e attestato da ChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests (che controlla l’ordine esatto dei metodi CDP e il parametro ['urls' => ['*']]). Questo è il blocco a livello di rete che le indicazioni OWASP SSRF raccomandano come controllo più robusto, oltre a un diniego totale a livello di trasporto coerente con NIST SP 800-53 SC-7.

Di conseguenza, un URL remoto di <img>, foglio di stile, font, script o iframe presente nell’input non viene caricato. Il bridge non implementa né un elenco di domini consentiti né un filtro di IP privati perché non ne ha bisogno: non consente alcun recupero di sottorisorse in uscita.

Nota sullo scostamento: il docblock di nextpdf/core per writeHtmlChrome() afferma che Chrome “recupererà risorse esterne” e consiglia di configurare una policy per “bloccare gli intervalli di IP privati e limitare i domini consentiti.” Questo descrive un modello configurabile con elenco di domini consentiti. La ChromeSecurityPolicy distribuita con Artisan non espone un elenco di domini consentiti e blocca invece tutte le richieste di sottorisorse in modo incondizionato. Fa fede il codice, non il docblock del core. Questo scostamento è registrato per il team della documentazione del core.

ChromeSecurityPolicy::validate() viene eseguito prima di contattare Chrome e rifiuta:

ControlloLimiteMotivazione
Dimensione HTML> maxHtmlSize (valore predefinito 5 MB)Limite contro l’esaurimento delle risorse (consumo incontrollato di risorse, CWE Top 25)
URI di dati Base64gruppo di acquisizione di >= 13_000_000 byteLimite contro la decompression bomb
<meta http-equiv="refresh">qualunque (senza distinzione tra maiuscole e minuscole, single/double)Blocca il reindirizzamento lato client verso endpoint interni, un vettore di navigazione SSRF

Il blocco del meta-refresh è un irrobustimento SSRF esplicito: senza questo controllo, un HTML fornito da un utente malintenzionato potrebbe reindirizzare Chrome verso un endpoint di metadati cloud prima di printToPDF. Il comportamento ai limiti è attestato in ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).

Inoltre, ChromeSecurityPolicy::wrapHtml() rimuove </style> da defaultCss prima dell’iniezione, per impedire una fuga dal blocco di stile verso il contesto di script (attestato da ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).

Il limite della sandbox di Chrome, indicato in modo esplicito

Sezione intitolata “Il limite della sandbox di Chrome, indicato in modo esplicito”

La sandbox del sistema operativo di Chrome è un controllo separato dalle barriere di rete sopra descritte e il bridge non la garantisce.

  • Per impostazione predefinita, noSandbox è false, quindi Chrome viene avviato con la propria sandbox abilitata. Il bridge non implementa la sandbox; si affida alla sandbox del binario di Chrome, che dipende dal supporto del kernel dell’host.
  • Impostare noSandbox: true avvia Chrome con --no-sandbox. Ciò rimuove la sandbox di isolamento dei processi di Chrome. L’opzione è prevista per i container in cui la sandbox non può essere inizializzata. Costituisce una riduzione effettiva dell’isolamento: una compromissione del renderer non è più contenuta dalla sandbox di Chrome.
  • Le barriere di rete del bridge (CSP + blocco CDP) restano in vigore indipendentemente dal fatto che la sandbox sia abilitata, ma non sostituiscono l’isolamento dei processi. Si applicano le indicazioni OWASP ASVS sul privilegio minimo: eseguire Chrome come utente non root, in un container vincolato, con noSandbox solo dove inevitabile, e trattare una distribuzione con --no-sandbox come un requisito di maggiore attendibilità sull’input.

Questa documentazione non afferma che il bridge sia “sicuro per impostazione predefinita”, “a prova di manomissione”, né che disabilitare la sandbox sia sicuro. Indica i controlli esistenti e il loro perimetro. Il provisioning di un container con sandbox è trattato nella pagina /integrations/artisan/chrome-renderer-setup/.

Elencate in src/Artisan/Exception/ e nel codice render/transport:

CondizionePresentata comeOrigine
chrome-php/chrome assenteChromeNotAvailableException (con comando di installazione)BrowserPool::getBrowser()
L’HTML supera maxHtmlSizeRuntimeException (“exceeds maximum allowed size”)ChromeSecurityPolicy::validate()
URI di dati base64 sovradimensionatoRuntimeException (“oversized base64 data URI”)ChromeSecurityPolicy::validate()
Meta-refresh vietatoRuntimeException (“forbidden meta refresh redirect”)ChromeSecurityPolicy::validate()
Avvio / timeout / arresto anomalo di ChromeChromeRenderException (che incapsula la causa)ChromeHtmlRenderer::render()
Chrome ha restituito un PDF vuotoChromeRenderException (“returned empty data”)ChromeHtmlRenderer::render()
La pagina non ha alcun flusso di contenutoPdfParseExceptionPageImporter::import()

Una ChromeRenderException sollevata all’interno del rendering viene rilanciata invariata. Qualsiasi altro Throwable viene incapsulato come ChromeRenderException, preservando l’eccezione precedente (attestato da ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping e ::renderWrapsUnexpectedThrowablesWithChromeRenderException). La pagina di Chrome viene sempre chiusa in un blocco finally, anche in caso di errore.

  • Dimensione dell’input: maxHtmlSize (valore predefinito 5 MB) e il limite di 13 MB per gli URI di dati base64.
  • Tempo: renderTimeout, in secondi, limita sia il caricamento del contenuto sia le chiamate CDP sincrone. I comandi di controllo CDP utilizzano un timeout fisso di 5 secondi.
  • Processo: BrowserPool riavvia Chrome ogni 100 rendering per limitare la crescita della memoria e chiude il processo su close() / distruzione.

Si tratta di limiti, non di quote. Un limite di risorse a livello di host (cgroup, ulimit, budget delle richieste) resta consigliato per qualsiasi percorso esposto a input non attendibile, coerentemente con le indicazioni della CWE Top 25 sul consumo di risorse.

Iniettare un logger PSR-3 per acquisire: avvio del rendering (dimensione, larghezza, altezza), completamento del rendering (dimensione dell’output, altezza del contenuto), avvio del browser (percorso del binario), riavvio del browser (conteggio dei rendering), chiusura del browser (conteggio dei rendering). Questi sono gli unici eventi emessi e non contengono alcun payload. Usarli per gli SLO di latenza e gli avvisi sulla frequenza di riavvio.

AsserzioneRiferimentoclause_idreference_id
Le richieste in uscita dai componenti server devono essere controllateOWASP ASVS 5.0§ (controllo SSRF/in uscita)
SSRF = il server recupera un URL fornito senza convalidare la destinazioneCWE Top 25 2025 (CWE-918)cwe_top25_2025#x28.x2.p2
L’SSRF (CWE-918) è una debolezza della CWE Top 25CWE Top 25 2025cwe_top25_2025#x1.p73
Il consumo incontrollato di risorse è una debolezza della CWE Top 25CWE Top 25 2025 (CWE-400)cwe_top25_2025#x19.x2.p2
Protezione del limite con diniego predefinito (autorizzazione per eccezione)NIST SP 800-53 Rev 5 SC-7SC-7
Il diniego a livello di rete delle chiamate verso destinazioni arbitrarie è il controllo SSRF robustoOWASP Cheat Sheet Series (SSRF Prevention §Network layer)owasp_cheatsheet_series#x132.x2
Proteggere dall’SSRF i componenti che recuperano URLOWASP Cheat Sheet Series§ (prevenzione SSRF, strumenti di recupero URL)
Isolare il rendering di contenuti non attendibili, privilegio minimoOWASP ASVS 5.0§ (sandbox / privilegio minimo)
Registrare gli eventi operativi; mantenere i payload fuori dai logNIST SP 800-92§ (indicazioni sul contenuto dei log)

Le citazioni sono state recuperate tramite il compliance engine di NextPDF (manifest del corpus 1d05b7c4…d790b6); il testo delle clausole è parafrasato, mai citato testualmente.

MinacciaControlloRischio residuo
SSRF tramite sottorisorsa remotaCSP default-src 'none' + CDP setBlockedURLs('*')Un bug del motore di Chrome che aggiri entrambe le barriere (la difesa in profondità riduce, non elimina)
SSRF tramite navigazione meta-refreshLa convalida pre-Chrome rifiuta il tagNuovo vettore di navigazione non corrispondente al pattern
Esaurimento delle risorseDimensione dell’input + limiti base64 + timeout + riavvio ogni 100 renderingNessuna quota per host; abbinare a cgroup/ulimit
Compromissione del processo del rendererSandbox di Chrome quando abilitatanoSandbox: true rimuove completamente questo controllo
Fuga dal blocco di stile / iniezione</style> rimosso da defaultCss; la CSP blocca gli scriptIniezione tramite un futuro vettore non rimosso

Il bridge non esegue alcuna operazione crittografica. Produce byte PDF tramite Chrome e li incorpora. Firma, cifratura e comportamento in modalità FIPS sono aspetti del core/Premium e non sono interessati da Artisan.

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