Seguridad y operaciones de Artisan
De un vistazo
Sección titulada «De un vistazo»El puente renderiza HTML potencialmente no confiable dentro de Chrome, detrás de dos barreras de red independientes y una política de contenido estricta. El sandbox del sistema operativo de Chrome es un control aparte y opcional, con límites declarados. Esta página documenta esa frontera; no afirma que sea absoluta.
Panorama conceptual
Sección titulada «Panorama conceptual»Un renderizado equivale a ejecutar una solicitud del lado del servidor: la aplicación entrega HTML a un motor de navegador que, de forma predeterminada, puede obtener recursos. Una obtención saliente dirigida por entradas no confiables es una falsificación de solicitudes del lado del servidor: CWE-918 la define como un servidor que recupera el contenido de una URL suministrada sin asegurar suficientemente que la solicitud llegue al destino previsto. La SSRF (CWE-918) es una debilidad del CWE Top 25. OWASP ASVS exige controlar, y no dejar implícitas, las solicitudes salientes de los componentes del servidor. La OWASP SSRF Prevention Cheat Sheet considera que la denegación estricta en la capa de red de llamadas a destinos arbitrarios es un control fuerte. La postura de red de denegación predeterminada que se describe a continuación es la respuesta del puente a ese requisito. NIST SP 800-53 SC-7 describe el mismo principio de frontera: denegar todo y permitir por excepción, que el puente aplica en la capa de transporte.
Residencia de datos y mitigaciones de PII
Sección titulada «Residencia de datos y mitigaciones de PII»El HTML que se pasa al puente se procesa íntegramente en el propio proceso y dentro de la instancia local de Chrome. El puente no realiza por sí mismo ninguna llamada de red saliente y bloquea cualquier intento de Chrome de hacerlas (consulta el modelo de red a continuación), por lo que el contenido de entrada no abandona el host a través del renderer. La PII presente en la entrada se renderiza en el PDF que produces: trata la salida con los mismos controles de residencia que la entrada. El puente no persiste la entrada ni la salida en disco; esa persistencia es responsabilidad de quien lo invoca.
Telemetría segura y registros de depuración
Sección titulada «Telemetría segura y registros de depuración»ChromeHtmlRenderer y BrowserPool aceptan un LoggerInterface PSR-3 opcional. El puente solo registra metadatos operativos: longitud en bytes de la entrada, ancho y alto de destino, longitud en bytes de la salida, alto de contenido medido, lanzamiento del navegador con la ruta del binario configurada, avisos de reinicio con un recuento de renderizados y eventos de cierre. No registra el contenido HTML, los bytes renderizados ni el texto extraído. Esto coincide con la guía de NIST SP 800-92: registrar eventos operativos manteniendo las cargas útiles sensibles fuera de los registros. La ruta del binario se registra; trátala como metadato de despliegue no sensible. La forma de las llamadas de registro queda verificada por tests/Unit/Artisan/ChromeHtmlRendererTest.php::renderLogsDebugWithSizeWidthHeightAndPdfSize y tests/Unit/Artisan/BrowserPoolTest.php::getBrowserLogsInfoOnLaunchWithBinaryPath.
Modelo de aislamiento de red (defensa en profundidad)
Sección titulada «Modelo de aislamiento de red (defensa en profundidad)»El puente aplica dos barreras independientes para que, si una se elude, el host no quede expuesto:
-
Content-Security-Policy. Cada renderizado se envuelve mediante
ChromeSecurityPolicy::wrapHtml()en un documento que incluye: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'deniega todos los orígenes de recursos.img-src data:permite únicamente imágenes en línea.navigate-to 'none'bloquea la navegación del lado del cliente.style-src 'unsafe-inline'es la única flexibilización necesaria para queprintToPDFde Chrome aplique estilos en línea. Verificado ensrc/Artisan/ChromeSecurityPolicy.phpy porChromeSecurityPolicyTest::wrapHtmlIncludesNavigationCspDirectives. -
Bloqueo de transporte CDP. Antes de cargar el contenido,
ChromeHtmlRendereremiteNetwork.enabley despuésNetwork.setBlockedURLscon el patrón['*'], lo que bloquea todas las URL de subrecursos en la capa de transporte de Chrome DevTools Protocol, independientemente de la CSP. Verificado ensrc/Artisan/ChromeHtmlRenderer::blockAllNetworkRequests()y porChromeHtmlRendererTest::renderAutoFitsHeightAndBlocksNetworkRequests(que comprueba el orden exacto de los métodos CDP y el parámetro['urls' => ['*']]). Este es el bloqueo de capa de red que la guía OWASP SSRF recomienda como control más fuerte, y una denegación total en la capa de transporte coherente con NIST SP 800-53 SC-7.
El resultado: si la entrada contiene una URL remota en un <img>, una hoja de estilos, una fuente, un script o un iframe, no se carga. El puente no implementa una lista de dominios permitidos ni un filtro de IP privadas porque no los necesita: no permite ninguna obtención saliente de subrecursos.
Nota de divergencia: el docblock de
nextpdf/coreenwriteHtmlChrome()afirma que Chrome «obtendrá recursos externos» y recomienda configurar una política para «bloquear rangos de IP privadas y limitar los dominios permitidos». Eso describe un modelo de lista de permitidos configurable. ElChromeSecurityPolicyde Artisan que se distribuye no expone una lista de permitidos y, en su lugar, bloquea todas las solicitudes de subrecursos de forma incondicional. El código, no el docblock del core, es la autoridad. Esta divergencia queda registrada para el equipo de documentación del core.
Validación de entrada (previa a Chrome)
Sección titulada «Validación de entrada (previa a Chrome)»ChromeSecurityPolicy::validate() se ejecuta antes de contactar con Chrome y rechaza:
| Comprobación | Límite | Justificación |
|---|---|---|
| Tamaño del HTML | > maxHtmlSize (predeterminado 5 MB) | Límite frente al agotamiento de recursos (consumo de recursos no controlado del CWE Top 25) |
| URI de datos en Base64 | grupo de captura de >= 13_000_000 bytes | Límite frente a bombas de descompresión |
<meta http-equiv="refresh"> | cualquiera (sin distinción de mayúsculas, comillas simples/dobles) | Bloquea la redirección del lado del cliente hacia endpoints internos: un vector de navegación de SSRF |
El bloqueo de meta-refresh es un endurecimiento explícito frente a SSRF: sin él, un HTML malicioso podría redirigir Chrome a un endpoint de metadatos en la nube antes de printToPDF. El comportamiento en los límites se verifica en ChromeSecurityPolicyTest (validateThrowsOnOversizedHtml, validateRejectsMetaRefreshRedirect, validateRejectsMetaRefreshCaseInsensitive, validateRejectsMetaRefreshWithSingleQuotes, validateRejectsOversizedBase64DataUri, validateRejectsBase64DataUriAtExactThreshold).
Además, ChromeSecurityPolicy::wrapHtml() elimina </style> de defaultCss antes de la inyección para impedir que el bloque de estilos escape al contexto de script (verificado por ChromeSecurityPolicyTest::wrapHtmlStripsStyleClosingTagsFromDefaultCss).
La frontera del sandbox de Chrome, declarada de forma explícita
Sección titulada «La frontera del sandbox de Chrome, declarada de forma explícita»El sandbox del sistema operativo de Chrome es un control aparte de las barreras de red anteriores, y el puente no lo garantiza.
- De forma predeterminada,
noSandboxesfalse, por lo que Chrome se lanza con su propio sandbox habilitado. El puente no implementa ese sandbox; depende del sandbox del binario de Chrome, que a su vez depende del soporte del kernel del host. - Establecer
noSandbox: truelanza Chrome con--no-sandbox. Esto elimina el sandbox de aislamiento de procesos de Chrome. Se proporciona para contenedores donde el sandbox no puede inicializarse. Es una reducción real del aislamiento: un renderer comprometido ya no queda contenido por el sandbox de Chrome. - Las barreras de red del puente (CSP + bloqueo CDP) siguen en vigor con el sandbox habilitado o deshabilitado, pero no sustituyen al aislamiento de procesos. Se aplica la guía de mínimo privilegio de OWASP ASVS: ejecuta Chrome como un usuario sin privilegios de root, en un contenedor restringido, con
noSandboxsolo cuando sea inevitable, y trata un despliegue con--no-sandboxcomo un requisito de mayor confianza en la entrada.
Esta documentación no afirma que el puente sea «seguro de forma predeterminada», «a prueba de manipulaciones» ni que deshabilitar el sandbox sea seguro. Declara los controles que existen y dónde terminan. El aprovisionamiento de un contenedor con sandbox se trata en la página /integrations/artisan/chrome-renderer-setup/.
Modos de fallo
Sección titulada «Modos de fallo»La lista se basa en src/Artisan/Exception/ y en el código de render/transport:
| Condición | Se manifiesta como | Origen |
|---|---|---|
Biblioteca chrome-php/chrome ausente | ChromeNotAvailableException (con comando de instalación) | BrowserPool::getBrowser() |
El HTML supera maxHtmlSize | RuntimeException («exceeds maximum allowed size») | ChromeSecurityPolicy::validate() |
| URI de datos en Base64 sobredimensionado | RuntimeException («oversized base64 data URI») | ChromeSecurityPolicy::validate() |
| Meta-refresh prohibido | RuntimeException («forbidden meta refresh redirect») | ChromeSecurityPolicy::validate() |
| Lanzamiento / tiempo de espera / caída de Chrome | ChromeRenderException (que envuelve la causa) | ChromeHtmlRenderer::render() |
| Chrome devolvió un PDF vacío | ChromeRenderException («returned empty data») | ChromeHtmlRenderer::render() |
| La página no tiene flujo de contenido | PdfParseException | PageImporter::import() |
Si se lanza una ChromeRenderException dentro del renderizado, se vuelve a lanzar sin cambios. Cualquier otro Throwable se envuelve como ChromeRenderException, conservando la excepción previa (verificado por ChromeHtmlRendererTest::renderRethrowsChromeRenderExceptionWithoutWrapping y ::renderWrapsUnexpectedThrowablesWithChromeRenderException). La página de Chrome siempre se cierra en un bloque finally, incluso si se produce un fallo.
Límites de recursos
Sección titulada «Límites de recursos»- Tamaño de la entrada:
maxHtmlSize(predeterminado 5 MB) y el límite de 13 MB para URI de datos en Base64. - Tiempo:
renderTimeoutsegundos limita tanto la carga del contenido como las llamadas síncronas de CDP. Los comandos de control de CDP usan un tiempo de espera fijo de 5 segundos. - Proceso:
BrowserPoolreinicia Chrome cada 100 renderizados para limitar el crecimiento de memoria y cierra el proceso al llamar aclose()/ al destruirse.
Estos son límites, no cuotas. Para cualquier ruta expuesta a entradas no confiables, se sigue recomendando un límite de recursos a nivel del host (cgroup, ulimit, presupuesto de solicitudes), coherente con la guía de consumo de recursos del CWE Top 25.
Ganchos de observabilidad
Sección titulada «Ganchos de observabilidad»Inyecta un logger PSR-3 para capturar: inicio del renderizado (tamaño, ancho, alto), renderizado completo (tamaño de salida, alto de contenido), lanzamiento del navegador (ruta del binario), reinicio del navegador (recuento de renderizados) y cierre del navegador (recuento de renderizados). Estos son los únicos eventos emitidos y no transportan contenido de la carga útil. Úsalos para SLO de latencia y alertas sobre la tasa de reinicios.
Conformidad
Sección titulada «Conformidad»| Afirmación | Referencia | clause_id | reference_id |
|---|---|---|---|
| Las solicitudes salientes de los componentes del servidor deben controlarse | OWASP ASVS 5.0 | § (SSRF/control de salientes) | |
| SSRF = el servidor recupera una URL suministrada sin validar el destino | CWE Top 25 2025 (CWE-918) | cwe_top25_2025#x28.x2.p2 | |
| La SSRF (CWE-918) es una debilidad del CWE Top 25 | CWE Top 25 2025 | cwe_top25_2025#x1.p73 | |
| El consumo de recursos no controlado es una debilidad del CWE Top 25 | CWE Top 25 2025 (CWE-400) | cwe_top25_2025#x19.x2.p2 | |
| Protección de frontera de denegación predeterminada (permitir por excepción) | NIST SP 800-53 Rev 5 SC-7 | SC-7 | |
| La denegación en la capa de red de las llamadas a destinos arbitrarios es el control fuerte frente a SSRF | OWASP Cheat Sheet Series (SSRF Prevention §capa de red) | owasp_cheatsheet_series#x132.x2 | |
| Proteger frente a SSRF los componentes que obtienen URL | OWASP Cheat Sheet Series | § (prevención de SSRF, herramientas de obtención de URL) | |
| Aislar el renderizado de contenido no confiable, mínimo privilegio | OWASP ASVS 5.0 | § (sandbox / mínimo privilegio) | |
| Registrar eventos operativos; mantener las cargas útiles fuera de los registros | NIST SP 800-92 | § (guía sobre el contenido de los registros) |
Las citas se recuperaron mediante el motor de conformidad de NextPDF (manifiesto del corpus 1d05b7c4…d790b6); el texto de las cláusulas se parafrasea, nunca se cita.
Modelo de amenazas
Sección titulada «Modelo de amenazas»| Amenaza | Control | Riesgo residual |
|---|---|---|
| SSRF a través de un subrecurso remoto | CSP default-src 'none' + CDP setBlockedURLs('*') | Un fallo del motor de Chrome que eluda ambas barreras (la defensa en profundidad reduce el riesgo, no lo elimina) |
| SSRF a través de navegación por meta-refresh | La validación previa a Chrome rechaza la etiqueta | Un nuevo vector de navegación no detectado por el patrón |
| Agotamiento de recursos | Límites de tamaño de entrada + límites de base64 + tiempo de espera + reinicio cada 100 renderizados | Sin cuota por host; combínalo con cgroup/ulimit |
| Compromiso del proceso renderer | Sandbox de Chrome cuando está habilitado | noSandbox: true elimina este control por completo |
| Fuga de estilos / inyección | </style> eliminado en defaultCss; la CSP bloquea el script | Inyección a través de un futuro vector no eliminado |
Comportamiento en modo FIPS
Sección titulada «Comportamiento en modo FIPS»El puente no realiza ninguna operación criptográfica. Produce bytes de PDF mediante Chrome y los incrusta. La firma, el cifrado y el comportamiento en modo FIPS son asuntos del core/Premium; Artisan no los afecta.
Consulta también
Sección titulada «Consulta también»- /integrations/artisan/configuration/
- /integrations/artisan/chrome-renderer-setup/
- /integrations/artisan/troubleshooting/
- /integrations/artisan/production-usage/
- /integrations/artisan/overview/