Ir al contenido

Contratos / Política de seguridad

El dominio de políticas de seguridad agrupa tres contratos que deniegan de forma predeterminada: CryptoPolicyInterface controla la elección de algoritmos y claves, HtmlSecurityPolicyInterface restringe la superficie funcional de HTML y ExternalResourcePolicyInterface gobierna la carga de recursos remotos. Cada uno es un contrato, de modo que un despliegue puede suministrar una política más estricta sin hacer un fork.

Ventana de terminal
composer require nextpdf/core:^3

CryptoPolicyInterface es el punto de control de cifrado. El core la consulta antes de cualquier paso de firma, cifrado o hash. La comprobación cubre el hash, el OID de la firma, el cifrador y la fortaleza de la clave. El contrato también informa del hash mínimo y de un nombre de política para el registro de auditoría. Aplica un conjunto de reglas como FIPS 140-3 o eIDAS. El código de firma y cifrado permanece sin cambios. Cuando no se establece ninguna política, se permite cualquier algoritmo. En un sitio regulado se debe establecer una política explícita.

HtmlSecurityPolicyInterface opera en la capa de análisis de HTML. Se ejecuta antes de que el contenido llegue a cualquier renderer. Indica si se permite una etiqueta, un atributo, una propiedad CSS o un esquema de URL. También limita el tamaño de la entrada y la profundidad de anidamiento. Se combina con las políticas de transporte de cada renderer (Chrome, Cloudflare, Gotenberg), que establecen los límites de tamaño y los encabezados CSP. La política de HTML reduce la superficie de ataque de la capa de análisis. Una etiqueta eliminada nunca llega al diseño. Por lo tanto, un elemento inyectado no puede cambiar la salida. Cuando no se establece ninguna política, el valor predeterminado permite todo el conjunto de funciones.

ExternalResourcePolicyInterface indica si la pipeline de HTML puede obtener una fuente, una hoja de estilos o una imagen externa. También establece los límites para cada obtención. Su postura predeterminada es denegar todo. Cada opción está desactivada hasta que se activa. El contrato sigue el principio de privilegio mínimo. El HTML no confiable puede apuntar a una URL de un atacante. Controla la obtención de @font-face por esquema, tamaño y número de glifos. Controla @import por esquema, profundidad y tamaño total. Controla background-image mediante una lista de esquemas y una lista de permitidos de dominios con coincidencia exacta. Limita el tamaño de las URI de datos. También controla las referencias externas de SVG. El contrato establece que, en producción, siempre deben denegarse. Habilitan la falsificación de solicitudes y la inyección de scripts. La obtención abierta de URL es un vector de falsificación de solicitudes del lado del servidor. El control de acceso se elude modificando la URL, según el OWASP Top 10 2025. Los componentes deben provenir únicamente de fuentes oficiales a través de enlaces seguros.

TipoClaseMiembros claveEstabilidadDesde
CryptoPolicyInterfaceinterfaceisHashAlgorithmAllowed(), isSignatureAlgorithmAllowed(), isEncryptionAlgorithmAllowed(), isKeyStrengthAllowed(), getPreferredHashAlgorithm(), getName()stable1.9.0
HtmlSecurityPolicyInterfaceinterfaceisTagAllowed(), isAttributeAllowed(), isCssPropertyAllowed(), isUrlSchemeAllowed(), getMaxInputSize(), getMaxNestingDepth(), getName()stable3.1.0
ExternalResourcePolicyInterfaceinterfaceisFontFaceAllowed(), getAllowedFontSchemes(), getMaxFontFileSize(), getMaxFontGlyphs(), isImportAllowed(), getMaxImportDepth(), isBackgroundImageAllowed(), getAllowedImageDomains(), getMaxDataUrlSize(), isSvgExternalReferenceAllowed()stable4.0.0

ExternalResourcePolicyInterface devuelve límites con tipos: tamaños positive-int, profundidad de importación int<1, 100> y listas de esquemas y dominios list<non-empty-string>. La implementación predeterminada deniega todas las capacidades.

examples/contracts/security-policy-quickstart.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
/**
* Decide whether a tag survives the policy.
*
* @param HtmlSecurityPolicyInterface $policy A core or custom policy.
*/
function tagSurvives(HtmlSecurityPolicyInterface $policy, string $tag): bool
{
return $policy->isTagAllowed($tag);
}

La función depende del contrato. Tanto una política restrictiva como la política predeterminada lo cumplen.

examples/contracts/security-policy-production.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Contracts\CryptoPolicyInterface;
use NextPDF\Contracts\ExternalResourcePolicyInterface;
use NextPDF\Contracts\HtmlSecurityPolicyInterface;
use Psr\Log\LoggerInterface;
final readonly class UntrustedHtmlGate
{
public function __construct(
private HtmlSecurityPolicyInterface $htmlPolicy,
private ExternalResourcePolicyInterface $resourcePolicy,
private CryptoPolicyInterface $cryptoPolicy,
private LoggerInterface $logger,
) {}
/**
* Reject input that exceeds the configured limits before rendering.
*
* @param string $html Untrusted HTML markup.
*/
public function assertAcceptable(string $html): void
{
$maxInput = $this->htmlPolicy->getMaxInputSize();
if ($maxInput > 0 && \strlen($html) > $maxInput) {
$this->logger->warning('HTML rejected: input over limit', [
'policy' => $this->htmlPolicy->getName(),
'limit' => $maxInput,
]);
throw new \LengthException('HTML input exceeds policy limit.');
}
if ($this->resourcePolicy->isSvgExternalReferenceAllowed()) {
$this->logger->error('Unsafe policy: SVG external references enabled.');
throw new \LogicException('SVG external references must be denied in production.');
}
}
}

La compuerta aplica el límite de entrada y rechaza una política de recursos insegura antes de ejecutar la pipeline. Registra el nombre de la política para la auditoría y lanza una excepción específica.

  • CryptoPolicyInterface permite cualquier algoritmo cuando no se establece ninguna política. El valor predeterminado abierto es una comodidad para el desarrollo, no una postura de producción. Se debe establecer una política explícita en cualquier despliegue regulado.
  • HtmlSecurityPolicyInterface::getMaxInputSize() devuelve 0 para indicar ausencia de límite. Tratar 0 como «sin límite de política», no como «rechazar todo», y aplicar también un límite en la capa de transporte.
  • ExternalResourcePolicyInterface deniega todo de forma predeterminada. Habilitar @font-face o background-image sin establecer una lista de esquemas abre una superficie de falsificación de solicitudes; se debe establecer la lista de permitidos cuando se habilite una capacidad.
  • Una lista de permitidos de dominios vacía en getAllowedImageDomains() significa que, una vez habilitadas las imágenes de fondo, se permiten todos los dominios. Una lista vacía no implica denegación; se deben suministrar dominios explícitos.
  • isSvgExternalReferenceAllowed() debe devolver false en producción. El contrato lo documenta; una política que devuelve true es un hallazgo, no una opción de configuración.

Una comprobación de política es una llamada a un predicado: O(1), sin coste proporcional al tamaño de la entrada. La política se consulta por cada etiqueta, por cada atributo, por cada propiedad CSS y por cada URL durante el análisis. Un documento patológico multiplica el número de llamadas. Cada llamada se mantiene en tiempo constante. El performance_budget de 1500 ms de reloj y 64 MB de pico está dominado por el análisis y el renderizado, no por la evaluación de la política. Los límites de tamaño de entrada y de profundidad de anidamiento existen para acotar el coste del analizador. Una política estricta mejora el rendimiento en el peor caso al rechazar antes del diseño un documento de tamaño excesivo o con anidamiento profundo.

Estos contratos son el perímetro defensivo del motor, por lo que el modelo de amenazas es explícito. La degradación de algoritmos se mitiga mediante CryptoPolicyInterface, que bloquea los hashes débiles y las claves cortas antes de cualquier operación. Las secuencias de comandos en sitios cruzados hacia PDF y la inyección de contenido se mitigan mediante HtmlSecurityPolicyInterface, que elimina las etiquetas, los atributos y el CSS no permitidos en la capa de análisis antes de que se ejecute el renderer. La falsificación de solicitudes del lado del servidor, las bombas de descompresión y las bombas de tamaño acumulado se mitigan mediante ExternalResourcePolicyInterface, que deniega todo de forma predeterminada y acota cada obtención por esquema, tamaño, profundidad y dominio. El agotamiento de recursos se mitiga mediante los límites de tamaño de entrada, profundidad de anidamiento, glifos de fuente y profundidad de importación. Como cada política es un contrato, un despliegue puede endurecer el perímetro sin hacer un fork del motor, y el nombre de la política se expone para el registro de auditoría. Tratar todo el HTML, todas las URL y todos los bytes de fuentes e imágenes como hostiles. Esta página está marcada como export_control_class: legal-review-required porque los contratos gobiernan la política criptográfica; la prosa parafrasea todas las fuentes normativas y no cita ninguna.

AfirmaciónEstándarCláusulaEvidencia
El manejo de URL sin restricciones permite eludir el control de acceso modificando la URL, lo que la política de recursos externos mitiga mediante valores predeterminados de denegación total y una lista de permitidos de dominios con coincidencia exacta.OWASP Top 10 2025A01
Los componentes externos deben obtenerse únicamente de fuentes oficiales a través de enlaces seguros, lo que la política aplica mediante listas de permitidos de esquemas.OWASP Top 10 2025Cadena de suministro de software

Ambos puntos están parafraseados a partir de las directrices de OWASP. El material de OWASP se referencia por cláusula; el motor no reproduce su texto.

El core define y congela los tres contratos de política, y suministra valores predeterminados permisivos para el desarrollo junto con una política de recursos estricta de denegación total por defecto. La edición Enterprise suministra un perfil FIPS 140-3 detrás de CryptoPolicyInterface, de modo que un despliegue regulado obtiene una postura de algoritmos validada sin cambiar el código de firma o cifrado. La superficie del contrato es idéntica en todas las ediciones. La diferencia es la implementación de política que inyecta cada despliegue.