Sicurezza e operazioni di Artisan
In sintesi
Sezione intitolata “In sintesi”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.
Panoramica concettuale
Sezione intitolata “Panoramica concettuale”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.
Telemetria sicura e scrubbing dei log
Sezione intitolata “Telemetria sicura e scrubbing dei log”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:
-
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éprintToPDFdi Chrome applichi gli stili inline. Verificato insrc/Artisan/ChromeSecurityPolicy.phpe attestato daChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives. -
Blocco del trasporto CDP. Prima di caricare il contenuto,
ChromeHtmlRendererinviaNetwork.enablequindiNetwork.setBlockedURLscon il pattern['*'], bloccando ogni URL di sottorisorsa al livello di trasporto del Chrome DevTools Protocol, indipendentemente dalla CSP. Verificato insrc/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests()e attestato daChromeHtmlRendererTest::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/coreperwriteHtmlChrome()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. LaChromeSecurityPolicydistribuita 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.
Convalida dell’input (pre-Chrome)
Sezione intitolata “Convalida dell’input (pre-Chrome)”ChromeSecurityPolicy::validate() viene eseguito prima di contattare Chrome e rifiuta:
| Controllo | Limite | Motivazione |
|---|---|---|
| Dimensione HTML | > maxHtmlSize (valore predefinito 5 MB) | Limite contro l’esaurimento delle risorse (consumo incontrollato di risorse, CWE Top 25) |
| URI di dati Base64 | gruppo di acquisizione di >= 13_000_000 byte | Limite 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: trueavvia 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
noSandboxsolo dove inevitabile, e trattare una distribuzione con--no-sandboxcome 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/.
Modalità di errore
Sezione intitolata “Modalità di errore”Elencate in src/Artisan/Exception/ e nel codice render/transport:
| Condizione | Presentata come | Origine |
|---|---|---|
chrome-php/chrome assente | ChromeNotAvailableException (con comando di installazione) | BrowserPool::getBrowser() |
L’HTML supera maxHtmlSize | RuntimeException (“exceeds maximum allowed size”) | ChromeSecurityPolicy::validate() |
| URI di dati base64 sovradimensionato | RuntimeException (“oversized base64 data URI”) | ChromeSecurityPolicy::validate() |
| Meta-refresh vietato | RuntimeException (“forbidden meta refresh redirect”) | ChromeSecurityPolicy::validate() |
| Avvio / timeout / arresto anomalo di Chrome | ChromeRenderException (che incapsula la causa) | ChromeHtmlRenderer::render() |
| Chrome ha restituito un PDF vuoto | ChromeRenderException (“returned empty data”) | ChromeHtmlRenderer::render() |
| La pagina non ha alcun flusso di contenuto | PdfParseException | PageImporter::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.
Limiti delle risorse
Sezione intitolata “Limiti delle risorse”- 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:
BrowserPoolriavvia Chrome ogni 100 rendering per limitare la crescita della memoria e chiude il processo suclose()/ 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.
Hook di osservabilità
Sezione intitolata “Hook di osservabilità”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.
Conformità
Sezione intitolata “Conformità”| Asserzione | Riferimento | clause_id | reference_id |
|---|---|---|---|
| Le richieste in uscita dai componenti server devono essere controllate | OWASP ASVS 5.0 | § (controllo SSRF/in uscita) | |
| SSRF = il server recupera un URL fornito senza convalidare la destinazione | CWE Top 25 2025 (CWE-918) | cwe_top25_2025#x28.x2.p2 | |
| L’SSRF (CWE-918) è una debolezza della CWE Top 25 | CWE Top 25 2025 | cwe_top25_2025#x1.p73 | |
| Il consumo incontrollato di risorse è una debolezza della CWE Top 25 | CWE 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-7 | SC-7 | |
| Il diniego a livello di rete delle chiamate verso destinazioni arbitrarie è il controllo SSRF robusto | OWASP Cheat Sheet Series (SSRF Prevention §Network layer) | owasp_cheatsheet_series#x132.x2 | |
| Proteggere dall’SSRF i componenti che recuperano URL | OWASP Cheat Sheet Series | § (prevenzione SSRF, strumenti di recupero URL) | |
| Isolare il rendering di contenuti non attendibili, privilegio minimo | OWASP ASVS 5.0 | § (sandbox / privilegio minimo) | |
| Registrare gli eventi operativi; mantenere i payload fuori dai log | NIST 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.
Modello di minaccia
Sezione intitolata “Modello di minaccia”| Minaccia | Controllo | Rischio residuo |
|---|---|---|
| SSRF tramite sottorisorsa remota | CSP 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-refresh | La convalida pre-Chrome rifiuta il tag | Nuovo vettore di navigazione non corrispondente al pattern |
| Esaurimento delle risorse | Dimensione dell’input + limiti base64 + timeout + riavvio ogni 100 rendering | Nessuna quota per host; abbinare a cgroup/ulimit |
| Compromissione del processo del renderer | Sandbox di Chrome quando abilitata | noSandbox: true rimuove completamente questo controllo |
| Fuga dal blocco di stile / iniezione | </style> rimosso da defaultCss; la CSP blocca gli script | Iniezione tramite un futuro vettore non rimosso |
Comportamento in modalità FIPS
Sezione intitolata “Comportamento in modalità FIPS”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.
Vedere anche
Sezione intitolata “Vedere anche”- /integrations/artisan/configuration/
- /integrations/artisan/chrome-renderer-setup/
- /integrations/artisan/troubleshooting/
- /integrations/artisan/production-usage/
- /integrations/artisan/overview/