Seguridad y operaciones
De un vistazo
Sección titulada «De un vistazo»Este puente envía su HTML a través de un límite de red hacia un motor de navegador. Esta página documenta cada control que protege ese límite, descrito a partir del código fuente. Cuando un control cita un estándar, la cita corresponde a lo que declara el propio docblock del código. Esta página reformula la afirmación del código y no reconstruye la redacción normativa.
Modelo de amenazas
Sección titulada «Modelo de amenazas»Los propios docblocks del paquete identifican las amenazas frente a las que ofrece defensa:
- XSS-a-PDF — marcado hostil que se ejecuta durante la representación.
- SSRF — marcado o una URL de destino que dirige una solicitud hacia una dirección interna.
- Agotamiento de recursos — entrada de tamaño excesivo o una bomba de descompresión.
- Reenlazado de DNS — un nombre de host que supera la validación y luego se resuelve a una dirección privada en el momento de la conexión.
- Interceptación TLS en ruta — un certificado sustituido en la ruta hacia el Worker.
Cada una se aborda más abajo mediante un control específico y comprobable.
Controles de entrada (antes de que la solicitud salga de PHP)
Sección titulada «Controles de entrada (antes de que la solicitud salga de PHP)»CloudflareSecurityPolicy::validate() se ejecuta antes de construir cualquier solicitud:
| Control | Comportamiento | Origen del límite |
|---|---|---|
| Límite de tamaño | Rechaza el HTML que supera maxHtmlSize | CloudflareRendererConfig, valor predeterminado 5000000 bytes |
| Protección contra bombas de descompresión Base64 | Estima el tamaño decodificado de cada URI data:…;base64,…; rechaza las que alcanzan o superan el límite | MAX_DATA_URI_BYTES = 13631488 |
| Prohibición de meta-refresh | Rechaza cualquier <meta http-equiv="refresh">, sin distinguir mayúsculas y minúsculas | expresión regular en CloudflareSecurityPolicy |
Una infracción lanza RuntimeException con un mensaje que indica el valor infractor y el límite. La prohibición de meta-refresh existe porque una directiva de refresco puede provocar una navegación desde dentro de la página que el Worker representa: un vector de SSRF que reside en el contenido, no en la URL.
La política de seguridad de HTML de nextpdf/core (HtmlSecurityPolicyInterface, valor predeterminado DefaultHtmlSecurityPolicy) opera en la capa de análisis y es complementaria a las comprobaciones de la capa de transporte anteriores. Se recupera con getHtmlSecurityPolicy(). Se puede inyectar una personalizada a través del constructor.
Controles de destino (SSRF y reenlazado de DNS)
Sección titulada «Controles de destino (SSRF y reenlazado de DNS)»CloudflareSecurityPolicy::validateWorkerUrl():
- Rechaza una URL que no se analice o que carezca de scheme/host (
Invalid Worker URL). - Rechaza cualquier esquema que no sea HTTPS (
Worker URL must use HTTPS). - Para un host con IP literal, rechaza los rangos privados o reservados mediante
el
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGEde PHP. En la práctica, esto rechaza el espacio privado RFC 1918, el bucle local (loopback) y las direcciones de enlace local RFC 3927. Las pruebas ejercitan explícitamente los rechazos de192.168.x,127.0.0.1y169.254.x. La extensión filter de PHP decide la pertenencia al rango, no una cláusula fijada por este paquete; RFC 1918 y RFC 3927 se nombran aquí de forma descriptiva como las definiciones bien conocidas de esos rangos. - Para un nombre de host, resuelve todos los registros A y AAAA mediante
dns_get_record()(nogethostbyname(), que solo devuelve la primera respuesta) y rechaza si cualquier dirección resuelta es privada o reservada.
El uso de la resolución de todos los registros es deliberado y está documentado en el docblock de la clase como una defensa frente a un host que devuelve varios registros, donde una búsqueda de un solo registro podría elegir el público mientras que la conexión posterior elige uno privado. Esto coincide con la OWASP SSRF Prevention Cheat Sheet, que indica que una aplicación debe recuperar todas las direcciones IP detrás del nombre de dominio (registros A y AAAA) y aplicar a cada una la comprobación de dirección no pública.
validateWorkerUrl() devuelve el conjunto de IP verificadas. A continuación, el renderizador llama a assertPinsStillValid() justo antes de enviar. Esa llamada vuelve a resolver el host y rechaza la solicitud si ha aparecido una nueva IP desde la validación (Worker URL DNS answer changed since validation — possible DNS rebinding attack). Esto cierra la ventana entre el tiempo de comprobación y el tiempo de uso que existe entre la validación y la conexión.
Controles de transporte (PinnedCurlTransport)
Sección titulada «Controles de transporte (PinnedCurlTransport)»Cuando existe un conjunto de IP verificadas o un conjunto de pins SPKI y se proporciona una ResponseFactory PSR-17, el renderizador usa Transport\PinnedCurlTransport en lugar del cliente PSR-18 inyectado. El transporte impone lo siguiente en la capa del manejador de cURL:
- DNS fijado —
CURLOPT_RESOLVEvincula host:puerto al conjunto de IP verificadas, de modo que libcurl no realiza su propia búsqueda en el momento de la conexión. Esto es lo que hace que la comprobación de DNS en espacio de usuario vincule realmente la conexión; sin ello, libcurl podría resolver una dirección distinta. - Fijación de clave pública TLS —
CURLOPT_PINNEDPUBLICKEYse establece a partir del conjunto combinado de pins. Esto sigue la RFC 7469 §2.6: una conexión fijada se acepta cuando el conjunto de huellas SPKI presentadas por el servidor tiene intersección con el conjunto de pins configurado, y un fallo de validación de pin se trata como no recuperable. Las cadenas de pin se normalizan desha256/<base64>a la formasha256//<base64>de cURL; un pin malformado lanzaInvalidSpkiPinException. - Verificación TLS activada —
CURLOPT_SSL_VERIFYPEER => true,CURLOPT_SSL_VERIFYHOST => 2. - Sin redirecciones automáticas —
CURLOPT_FOLLOWLOCATION => false,CURLOPT_MAXREDIRS => 0. Una respuesta 3xx se expone a la capa de política en lugar de que libcurl la siga hacia un host no verificado. El docblock de la clase indica que esta es una elección deliberada para que las redirecciones se vuelvan a validar y no se sigan en silencio. - Tiempo de espera estricto —
CURLOPT_TIMEOUTse establece a partir derenderTimeout(valor predeterminado30segundos).
Un error de cURL o un cuerpo que no sea una cadena lanza CloudflareRenderException con el número y el mensaje de error de cURL.
Guía operativa de fijación
Sección titulada «Guía operativa de fijación»La configuración incluye pinnedPublicKeys y un backupPublicKeys aparte. La RFC 7469 §2.5 describe un pin de respaldo — una huella de un par de claves secundario, aún no desplegado y mantenido sin conexión — como la principal forma de recuperarse de un fallo involuntario de validación de pin. Mantener al menos un pin de respaldo para que la rotación de certificados no inutilice el endpoint sigue esa guía. El campo aparte permite validar una rotación de forma independiente. En términos operativos:
- Fijar la SPKI del certificado de hoja o de un intermedio cuya rotación controle.
- Configurar siempre un pin de respaldo para el siguiente certificado antes de rotar.
- Un conjunto de pins vacío deshabilita la fijación; usarlo solo con una cadena de certificados estable y conocida. La fijación es opcional mediante configuración.
Autenticación y manejo de secretos
Sección titulada «Autenticación y manejo de secretos»- La solicitud al Worker lleva
Authorization: Bearer <apiToken>.apiTokenes#[SensitiveParameter], por lo que se oculta en los rastreos de pila. La sonda de accesibilidad envía la misma cabecera bearer en unHEADHTTP. - Las claves de acceso de R2 (
accessKeyId,secretAccessKey) son#[SensitiveParameter]y se usan únicamente para derivar la clave de firma de AWS Signature V4. ApiKeyValidatorcompara las claves conhash_equals()(seguro frente a temporización) y admite el almacenamiento de claves con hash SHA-256 mediantevalidateHashed().- Los objetos de configuración son
final readonly— un secreto establecido una vez no se puede mutar. - Obtener los secretos de variables de entorno o de un gestor de secretos. Nunca confirmarlos en el control de versiones. El paquete sigue la línea base de seguridad más amplia de NextPDF: PHPStan Level 10,
declare(strict_types=1)en cada archivo, sineval()/exec(), y GitHub Actions fijadas por SHA.
Lo que este paquete no afirma
Sección titulada «Lo que este paquete no afirma»- No declara ningún límite de la plataforma Cloudflare (tiempo de CPU del Worker, memoria, techo del cuerpo de la solicitud ni número de subsolicitudes). Los únicos límites de tamaño y tiempo que esta documentación declara son los que impone el propio paquete, enumerados arriba y en /integrations/cloudflare/configuration/. Para los límites de la plataforma, consultar la documentación oficial de Cloudflare y la propia implementación de su Worker.
- No firma PDF ni hace ninguna afirmación de conformidad de firma. Cuando se requieran firmas, representar aquí y luego firmar con el motor. NextPDF Pro proporciona únicamente la firma PAdES B-B; los perfiles de validación a largo plazo son una capacidad Enterprise y quedan fuera del alcance de este puente.
- No certifica ni garantiza que la canalización sea «a prueba de manipulaciones», ni la convierte en tal. Implementa los controles específicos y verificables a partir del código fuente descritos en esta página y nada más allá de ellos.
Manual operativo
Sección titulada «Manual operativo»| Síntoma | Primera comprobación |
|---|---|
Worker URL must use HTTPS | El esquema del workerUrl configurado. |
private or reserved IP | Los registros DNS del nombre de host del Worker; un registro que se resuelve en el espacio RFC 1918 / loopback / RFC 3927. |
DNS answer changed since validation | Inestabilidad de DNS o un intento de reenlazado; volver a resolver e inspeccionar el conjunto de registros. |
cURL transport error | La ruta de red, la cadena TLS y — si hay pins establecidos — si la SPKI del certificado servido sigue estando en el conjunto de pins. |
| Las representaciones fallan justo después de una rotación de certificado | Un conjunto de pins sin un pin de respaldo coincidente. Añadir la nueva SPKI como respaldo antes de rotar. |
is not installed / no LocalRendererFactoryInterface | Respaldo habilitado pero sin ninguna fábrica conectada, o nextpdf/artisan ausente. |
| Denegaciones de límite de tasa inconsistentes entre nodos | El limitador en memoria es por proceso; anteponerle un almacén compartido. |
Notificación de incidentes
Sección titulada «Notificación de incidentes»Notificar las vulnerabilidades a través de GitHub Security Advisories o del contacto de seguridad del archivo SECURITY.md del repositorio. No registrar incidencias de seguridad como issues públicos de GitHub.
Véase también
Sección titulada «Véase también»- /integrations/cloudflare/overview/ — por qué el paquete está estructurado en torno a este límite.
- /integrations/cloudflare/configuration/ — campos de conjunto de pins y de límites.
- /integrations/cloudflare/troubleshooting/ — mapeo completo de fallo a excepción.