Ir al contenido

Seguridad y operaciones del paquete Laravel de NextPDF

El paquete aplica un conjunto fijo de encabezados de respuesta, sanea los nombres de archivo de descarga, valida en el worker las rutas de salida de la cola y encapsula el HTTP de la autoridad de marca de tiempo mediante un cliente con protección frente a la falsificación de solicitudes. Esta página presenta el modelo de amenazas y la configuración de despliegue que requiere cada control.

Ventana de terminal
composer require nextpdf/laravel
php artisan vendor:publish --tag=nextpdf-config

El paquete integra un motor PDF en un framework web. El límite de confianza está en la solicitud HTTP y en el transporte de la cola. Los controles que se describen a continuación abordan el manejo de las respuestas, las cargas útiles deserializadas de los jobs y el HTTP saliente hacia una autoridad de marca de tiempo.

ActivoAmenazaControl en este paqueteConfiguración de despliegue requerida
Respuesta HTTP del PDFInferencia del tipo de contenido, clickjacking, indexaciónConjunto fijo de encabezados en cada factory de PdfResponseNinguna; los encabezados no son configurables
Nombre de archivo de descargaInyección de encabezados, path traversal en Content-DispositionEl saneador de nombres de archivo elimina separadores, caracteres de control y bytes nulosNinguna; el saneador siempre se ejecuta
Ruta de salida del job de la colaEscritura arbitraria de archivos mediante una carga útil serializada manipuladaRuta validada en handle() en el workerDirigir la salida hacia una ruta de almacenamiento controlada
HTTP saliente hacia la TSAFalsificación de solicitudes del lado del servidor, manipulación en texto planoCliente HTTP con protección frente a la falsificación de solicitudes; HTTPS obligatorio salvo que se relaje explícitamenteMantener tsa.allow_insecure_http = false; fijar la SPKI
Estado compartido del workerFuga de estado entre solicitudes en workers de larga duraciónRegistro de fuentes bloqueado; caché de imágenes acotada; documento ligado al factoryEstablecer preload_fonts; acotar la memoria en el contenedor

Cada factory de PdfResponse establece un conjunto fijo de encabezados:

  • Cache-Control: private, max-age=0, must-revalidate
  • Pragma: public
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Content-Security-Policy: default-src 'none'
  • X-Robots-Tag: noindex, nofollow
  • Referrer-Policy: no-referrer

Estos valores son constantes en PdfResponse. No son configurables. La suite de pruebas del paquete verifica cada encabezado en cada método factory, incluidas las variantes de streaming.

El nombre de archivo de descarga se procesa con un saneador antes de llegar al encabezado Content-Disposition. El saneador elimina separadores de ruta, caracteres de control y bytes nulos, y emite un parámetro RFC 5987 filename*= para los nombres no ASCII. Un nombre de archivo vacío se convierte en document.pdf.

GeneratePdfJob serializa un closure a través del transporte de la cola. La ruta de salida se valida dentro de handle() en el worker, no durante el despacho. La validación rechaza:

  • bytes nulos en la ruta,
  • los esquemas de stream-wrapper (por ejemplo, php://),
  • los segmentos de path traversal ..,
  • cualquier ruta que no termine en .pdf (sin distinguir mayúsculas y minúsculas).

Cada rechazo lanza InvalidArgumentException. La validación se ejecuta en el punto de consumo. La carga útil serializada en un transporte de Redis o de base de datos podría alterarse antes de que el worker la lea. La ruta de salida debe dirigirse hacia un directorio de almacenamiento controlado; no debe derivarse de la entrada de solicitud sin validar.

HTTP saliente hacia una autoridad de marca de tiempo

Sección titulada «HTTP saliente hacia una autoridad de marca de tiempo»

Cuando se configura una autoridad de marca de tiempo, el paquete vincula un Psr\Http\Client\ClientInterface PSR-18. Un cliente PSR-18 envía una solicitud PSR-7 y devuelve una respuesta PSR-7 (PSR-18 §2). El cliente vinculado envuelve un cliente basado en curl con una capa con protección frente a la falsificación de solicitudes que obliga a usar HTTPS salvo que tsa.allow_insecure_http sea explícitamente verdadero.

La autoridad de marca de tiempo es una capacidad del nivel Premium. El paquete Core que se documenta aquí vincula el cliente HTTP y el cableado del cliente de marca de tiempo; la firma propiamente dicha requiere nextpdf/premium. Esta página no documenta el comportamiento de la línea base PAdES más allá de B-B; las líneas base superiores quedan fuera del alcance.

Guía operativa para la autoridad de marca de tiempo:

  1. Mantener tsa.allow_insecure_http con el valor false en producción.
  2. Establecer tsa.pinned_public_keys con los hashes SPKI SHA-256 en base64 del certificado de la autoridad de marca de tiempo (forma RFC 7469).
  3. Mantener tsa.warn_on_key_rotation con el valor true para que una SPKI modificada quede registrada antes de que el certificado fijado caduque.
  4. Tomar tsa.url únicamente de configuración de confianza. Si un operador puede establecerla desde una superficie de administración, aplicar una política de firewall de egreso o de DNS para reducir la exposición a la falsificación de solicitudes.

Usar Psr\Log\LoggerInterface para los diagnósticos. Pasar contexto estructurado, no cadenas interpoladas. PSR-3 delega el escape de los marcadores de posición en la implementación del logger e indica a quienes lo invocan que no preescapen los valores de contexto (PSR-3 §1.2). Registrar la clase de la excepción, no el mensaje ni la traza, para reducir el detalle interno en los registros.

resource: config/nextpdf.php (tsa hardening) + src/Laravel/NextPdfServiceProvider.php
<?php
declare(strict_types=1);
// .env — production timestamp-authority hardening
// NEXTPDF_TSA_URL=https://tsa.example.test
// NEXTPDF_TSA_ALLOW_INSECURE_HTTP=false
// NEXTPDF_TSA_WARN_ROTATION=true
return [
'tsa' => [
'url' => env('NEXTPDF_TSA_URL'),
'allow_insecure_http' => env('NEXTPDF_TSA_ALLOW_INSECURE_HTTP', false),
'warn_on_key_rotation' => env('NEXTPDF_TSA_WARN_ROTATION', true),
'pinned_public_keys' => [
// base64 SHA-256 SPKI hashes of the TSA certificate
],
],
];
  • El conjunto de encabezados de respuesta es fijo. Las aplicaciones que necesiten una CSP diferente deben posprocesar la respuesta después de que el factory la devuelva.
  • La validación de la ruta se ejecuta en el worker. Una ruta incorrecta pasa dispatch() y solo falla cuando el job se ejecuta.
  • tsa.allow_insecure_http = true elimina la obligatoriedad de HTTPS y debilita la confianza en la marca de tiempo. Restringirlo al desarrollo local.
  • El registro de fuentes queda bloqueado después del calentamiento; un intento de registrar una fuente en tiempo de ejecución dentro de un worker de larga duración se rechaza por diseño.

Los controles de seguridad son operaciones de cadenas y arreglos en tiempo constante y no añaden ningún costo medible por solicitud. El costo operativo dominante es el análisis de las fuentes en el primer uso; precargar las fuentes al arrancar el worker para evitar la latencia en la primera solicitud.

Esta página es la referencia del modelo de amenazas del paquete. Los controles que se describen aquí se aplican en el código fuente y los verifica la suite de pruebas. La configuración de despliegue que el operador debe aportar se señala en la tabla del modelo de amenazas y en los pasos de la autoridad de marca de tiempo.

AfirmaciónFuenteCláusulareference_id
El cliente PSR-18 envía una solicitud PSR-7 y devuelve una respuesta PSR-7Cliente HTTP PSR-18§2
Quien invoca pasa contexto de registro estructurado sin escaparLogger PSR-3§1.2

El fijado de SPKI de RFC 7469 se nombra como la forma que usa la clave de configuración tsa.pinned_public_keys; el paquete consume los valores de fijado aportados por el operador y no implementa la RFC propiamente dicha.

La firma PAdES B-B y la integración con la autoridad de marca de tiempo requieren nextpdf/premium. Es una capacidad Enterprise opcional; el paquete Core que se documenta aquí no necesita ningún cambio de código para adoptarla. Consultar https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/configuration/ — cada clave de TSA, de firma y de la cola
  • /integrations/laravel/production-usage/ — patrones de DI y de manejo de errores
  • /integrations/laravel/troubleshooting/ — por qué las comprobaciones de ruta rechazan la entrada
  • /integrations/laravel/boot-and-discovery/ — tiempos de vida de los bindings en workers de larga duración