Segurança e operações do Artisan
Visão geral
Seção intitulada “Visão geral”A ponte renderiza HTML potencialmente não confiável no Chrome, por trás de duas barreiras de rede independentes e de uma política de conteúdo rigorosa. O sandbox do sistema operacional do Chrome é um controle separado e opcional, com limites explícitos. Esta página documenta essa fronteira. Ela não afirma que a fronteira seja absoluta.
Visão conceitual
Seção intitulada “Visão conceitual”Uma renderização executa uma requisição no lado do servidor: a aplicação entrega o HTML a um mecanismo de navegador que, por padrão, pode buscar recursos. Quando uma entrada não confiável aciona uma busca de saída, o risco é a falsificação de requisição no lado do servidor (SSRF): a entrada CWE-918 do Common Weakness Enumeration (CWE) define isso como um servidor que recupera o conteúdo de uma URL fornecida sem garantia suficiente de que a requisição chegue ao destino esperado. A SSRF (CWE-918) é uma fraqueza do CWE Top 25. O Application Security Verification Standard (ASVS) do Open Worldwide Application Security Project (OWASP) exige que você controle as requisições de saída dos componentes do servidor, em vez de deixá-las implícitas. O OWASP SSRF Prevention Cheat Sheet trata a negação, na camada de rede, de chamadas a destinos arbitrários como o controle forte. A postura de rede com negação por padrão apresentada abaixo é a resposta da ponte a esse requisito. A SC-7 do National Institute of Standards and Technology (NIST) Special Publication (SP) 800-53 descreve o mesmo princípio de fronteira de negar tudo e permitir por exceção que a ponte aplica na camada de transporte.
Residência de dados e mitigações de PII
Seção intitulada “Residência de dados e mitigações de PII”O HTML passado à ponte é processado inteiramente no processo e dentro da instância local do Chrome. A ponte não faz nenhuma chamada própria de rede de saída e impede que o Chrome faça qualquer uma (consulte o modelo de rede abaixo), de modo que o conteúdo de entrada não sai do host por meio do renderizador. As informações de identificação pessoal (PII) presentes na entrada são renderizadas na saída em Portable Document Format (PDF) que você produz; portanto, trate a saída com os mesmos controles de residência da entrada. A ponte não persiste a entrada nem a saída em disco; a persistência é responsabilidade de quem chama.
Telemetria segura e limpeza de logs
Seção intitulada “Telemetria segura e limpeza de logs”ChromeHtmlRenderer e BrowserPool aceitam um LoggerInterface opcional de PHP Standard Recommendation (PSR)-3. A ponte registra apenas metadados operacionais: o comprimento em bytes da entrada, a largura e a altura de destino, o comprimento em bytes da saída, a altura de conteúdo medida, a inicialização do navegador com o caminho do binário configurado, avisos de reinício com contagem de renderizações e eventos de fechamento. Ela não registra conteúdo HTML, bytes renderizados nem texto extraído. Isso está alinhado com a orientação da NIST SP 800-92 de registrar eventos operacionais mantendo cargas sensíveis fora dos logs. O caminho do binário é registrado. Trate-o como metadado de implantação não sensível. Os testes verificam os formatos das chamadas de log em tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize e tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.
Modelo de isolamento de rede (defesa em profundidade)
Seção intitulada “Modelo de isolamento de rede (defesa em profundidade)”A ponte aplica duas barreiras independentes para que uma única fronteira não exponha o host:
-
Content-Security-Policy. Toda renderização é envolvida por
ChromeSecurityPolicy::wrapHtml()em um documento que carrega:default-src 'none'; style-src 'unsafe-inline'; img-src data:;base-uri 'none'; form-action 'none'; frame-ancestors 'none';navigate-to 'none';A diretiva
default-src 'none'da Content Security Policy (CSP) nega todas as origens de recursos.img-src data:permite apenas imagens embutidas.navigate-to 'none'bloqueia a navegação no lado do cliente.style-src 'unsafe-inline'é a única flexibilização necessária para que oprintToPDFdo Chrome aplique estilos embutidos. Verificado emsrc/Artisan/ChromeSecurityPolicy.phpe assegurado porChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives. -
Bloqueio de transporte do Chrome DevTools Protocol (CDP). Antes de o conteúdo carregar,
ChromeHtmlRendereremiteNetwork.enablee, em seguida,Network.setBlockedURLscom o padrão['*']. Isso bloqueia todas as URLs de subrecursos na camada de transporte do Chrome DevTools Protocol, independentemente do CSP. Verificado emsrc/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests()e assegurado porChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests(que verifica a ordem exata dos métodos CDP e o parâmetro['urls' => ['*']]). Esse é o bloqueio na camada de rede que a orientação de SSRF da OWASP recomenda como o controle mais forte, e é uma negação total no nível de transporte, consistente com a SC-7 da NIST SP 800-53.
O resultado: uma URL remota de <img>, folha de estilo, fonte, script ou iframe presente na entrada não é carregada. A ponte não implementa lista de domínios permitidos nem filtro de IP privado porque não precisa de nenhum deles: ela não permite nenhuma busca de subrecurso de saída.
Nota de divergência: o docblock do
nextpdf/coreemwriteHtmlChrome()diz que o Chrome “buscará recursos externos” e recomenda configurar uma política para “bloquear faixas de IP privadas e limitar os domínios permitidos.” Isso descreve um modelo configurável de lista de permitidos. OChromeSecurityPolicydo Artisan distribuído não expõe uma lista de permitidos; ele bloqueia todas as requisições de subrecurso incondicionalmente. O código, e não o docblock do core, é a fonte autoritativa. Essa divergência está registrada com a equipe de documentação do core.
Validação de entrada (pré-Chrome)
Seção intitulada “Validação de entrada (pré-Chrome)”ChromeSecurityPolicy::validate() é executado antes de a ponte contatar o Chrome e rejeita:
| Verificação | Limite | Justificativa |
|---|---|---|
| Tamanho do HTML | > maxHtmlSize (padrão 5 MB) | Limite contra exaustão de recursos (consumo descontrolado de recursos do CWE Top 25) |
| URI de dados base64 | grupo de captura de >= 13_000_000 bytes | Limite contra bomba de descompressão |
<meta http-equiv="refresh"> | qualquer (sem distinção de maiúsculas/minúsculas, aspas single/double) | Bloqueia redirecionamentos no lado do cliente para endpoints internos, um vetor de navegação SSRF |
O bloqueio de meta-refresh é um endurecimento explícito contra SSRF. Sem ele, um HTML controlado por um invasor poderia redirecionar o Chrome para um endpoint de metadados de nuvem antes do printToPDF. O comportamento da fronteira é assegurado em ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).
Além disso, ChromeSecurityPolicy::wrapHtml() remove </style> de defaultCss antes da injeção, para evitar uma saída do bloco de estilo para o contexto de script (assegurado por ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).
A fronteira do sandbox do Chrome — declarada explicitamente
Seção intitulada “A fronteira do sandbox do Chrome — declarada explicitamente”O sandbox do sistema operacional do Chrome é um controle separado das barreiras de rede acima, e a ponte não oferece garantia sobre ele.
- Por padrão,
noSandboxéfalse, de modo que o Chrome é iniciado com seu próprio sandbox ativado. A ponte não implementa esse sandbox; ela depende do sandbox do binário do Chrome, que por sua vez depende do suporte do kernel do host. - Definir
noSandbox: trueinicia o Chrome com--no-sandbox. Isso remove o sandbox de isolamento de processos do Chrome. A opção existe para contêineres em que o sandbox não consegue inicializar. É uma redução real de isolamento: um comprometimento do renderizador deixa de ser contido pelo sandbox do Chrome. - As barreiras de rede da ponte (CSP + bloqueio CDP) permanecem em vigor, esteja o sandbox ativado ou não, mas não substituem o isolamento de processos. A orientação de menor privilégio do OWASP ASVS se aplica: execute o Chrome como um usuário não root, em um contêiner restrito, use
noSandboxsomente quando for inevitável e trate uma implantação com--no-sandboxcomo um requisito de maior confiança na entrada.
Esta documentação não afirma que a ponte é “segura por padrão” ou “à prova de adulteração”. Ela também não afirma que desativar o sandbox seja seguro. Ela declara os controles existentes e onde eles param. O provisionamento de um contêiner com suporte a sandbox é abordado na página /integrations/artisan/chrome-renderer-setup/.
Modos de falha
Seção intitulada “Modos de falha”Estes modos de falha são enumerados a partir de src/Artisan/Exception/ e do código de render/transport:
| Condição | Apresentado como | Origem |
|---|---|---|
chrome-php/chrome (biblioteca) ausente | ChromeNotAvailableException (com comando de instalação) | BrowserPool::getBrowser() |
HTML excede maxHtmlSize | RuntimeException (“exceeds maximum allowed size”) | ChromeSecurityPolicy::validate() |
| URI de dados base64 com tamanho excessivo | RuntimeException (“oversized base64 data URI”) | ChromeSecurityPolicy::validate() |
| Meta-refresh proibido | RuntimeException (“forbidden meta refresh redirect”) | ChromeSecurityPolicy::validate() |
| Inicialização / tempo limite / falha do Chrome | ChromeRenderException (envolvendo a causa) | ChromeHtmlRenderer::render() |
| Chrome retornou PDF vazio | ChromeRenderException (“returned empty data”) | ChromeHtmlRenderer::render() |
| Página sem fluxo (stream) de conteúdo | PdfParseException | PageImporter::import() |
Se ChromeRenderException for lançada durante a renderização, ela é relançada sem alterações. Qualquer outro Throwable é envolvido como ChromeRenderException, preservando a exceção anterior (assegurado por ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping e ::renderWrapsUnexpectedThrowablesWithChromeRenderException). A página do Chrome é sempre fechada em um bloco finally, mesmo em caso de falha.
Limites de recursos
Seção intitulada “Limites de recursos”- Tamanho da entrada:
maxHtmlSize(padrão 5 MB) e o limite de 13 MB para URI de dados base64. - Tempo:
renderTimeoutsegundos limitam tanto o carregamento de conteúdo quanto as chamadas síncronas do CDP. Os comandos de controle do CDP usam um tempo limite fixo de 5 segundos. - Processo:
BrowserPoolreinicia o Chrome a cada 100 renderizações para limitar o crescimento de memória e encerra o processo emclose()/ destruição.
Esses são limites, não cotas. Para qualquer caminho exposto a entrada não confiável, ainda assim use limites de recursos no nível do host (cgroup, ulimit, orçamento de requisições), de forma consistente com a orientação de consumo de recursos do CWE Top 25.
Hooks de observabilidade
Seção intitulada “Hooks de observabilidade”Injete um logger PSR-3 para capturar o início da renderização (tamanho, largura, altura), a conclusão da renderização (tamanho da saída, altura do conteúdo), a inicialização do navegador (caminho do binário), o reinício do navegador (contagem de renderizações) e o fechamento do navegador (contagem de renderizações). Esses são os únicos eventos emitidos, e eles não carregam nenhum conteúdo de payload. Use-os para objetivos de nível de serviço (SLOs) de latência e para alertas de taxa de reinício.
Conformidade
Seção intitulada “Conformidade”| Afirmação | Referência | clause_id | reference_id |
|---|---|---|---|
| As requisições de saída dos componentes do servidor devem ser controladas | OWASP ASVS 5.0 | § (SSRF/controle de saída) | |
| SSRF = o servidor recupera uma URL fornecida sem validar o destino | CWE Top 25 2025 (CWE-918) | cwe_top25_2025#x28.x2.p2 | |
| A SSRF (CWE-918) é uma fraqueza do CWE Top 25 | CWE Top 25 2025 | cwe_top25_2025#x1.p73 | |
| O consumo descontrolado de recursos é uma fraqueza do CWE Top 25 | CWE Top 25 2025 (CWE-400) | cwe_top25_2025#x19.x2.p2 | |
| Proteção de fronteira com negação por padrão (permitir por exceção) | NIST SP 800-53 Rev 5 SC-7 | SC-7 | |
| A negação na camada de rede de chamadas a destinos arbitrários é o controle forte de SSRF | OWASP Cheat Sheet Series (SSRF Prevention §Network layer) | owasp_cheatsheet_series#x132.x2 | |
| Proteja contra SSRF os componentes que buscam URLs | OWASP Cheat Sheet Series | § (prevenção de SSRF, ferramentas de busca de URL) | |
| Isole a renderização de conteúdo não confiável, menor privilégio | OWASP ASVS 5.0 | § (sandbox / menor privilégio) | |
| Registre eventos operacionais; mantenha as cargas fora dos logs | NIST SP 800-92 | § (orientação sobre conteúdo de log) |
As citações foram obtidas por meio do mecanismo de conformidade do NextPDF (manifesto do corpus 1d05b7c4…d790b6); o texto das cláusulas é parafraseado, nunca citado literalmente.
Modelo de ameaças
Seção intitulada “Modelo de ameaças”| Ameaça | Controle | Risco residual |
|---|---|---|
| SSRF via subrecurso remoto | CSP default-src 'none' + CDP setBlockedURLs('*') | Um bug no mecanismo do Chrome que contorne as duas barreiras (a defesa em profundidade reduz o risco, mas não o elimina) |
| SSRF via navegação por meta-refresh | A validação pré-Chrome rejeita a tag | Um novo vetor de navegação não correspondido pelo padrão |
| Exaustão de recursos | Limites de tamanho da entrada + de base64 + tempo limite + reinício a cada 100 renderizações | Sem cota por host; combine com cgroup/ulimit |
| Comprometimento do processo do renderizador | Sandbox do Chrome quando ativado | noSandbox: true remove esse controle por completo |
| Fuga de estilo / injeção | </style> removido em defaultCss; o CSP bloqueia script | Injeção por meio de um vetor futuro que não seja removido |
Comportamento no modo FIPS
Seção intitulada “Comportamento no modo FIPS”A ponte não realiza nenhuma operação criptográfica. Ela produz bytes de PDF por meio do Chrome e os incorpora. Assinatura, criptografia e comportamento no modo Federal Information Processing Standards (FIPS) são questões do core/Premium e não são afetados pelo Artisan.
Consulte também
Seção intitulada “Consulte também”- /integrations/artisan/configuration/
- /integrations/artisan/chrome-renderer-setup/
- /integrations/artisan/troubleshooting/
- /integrations/artisan/production-usage/
- /integrations/artisan/overview/