Aller au contenu

Sécurité et exploitation du package NextPDF pour Laravel

Le package applique un jeu fixe d’en-têtes de réponse, assainit les noms de fichier proposés au téléchargement, valide les chemins de sortie de la file d’attente sur le worker et fait transiter le trafic HTTP vers l’autorité d’horodatage par un client tenant compte des falsifications de requête. Cette page énonce le modèle de menace et la configuration de déploiement exigée par chaque contrôle.

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

Ce package adapte un moteur PDF à un framework web. La frontière de confiance se situe au niveau de la requête HTTP et du transport de la file d’attente. Les contrôles ci-dessous couvrent la gestion des réponses, les charges utiles de tâches désérialisées et le trafic HTTP sortant vers une autorité d’horodatage.

ActifMenaceContrôle dans ce packageConfiguration de déploiement requise
Réponse HTTP du PDFDétection du type de contenu, clickjacking, indexationJeu fixe d’en-têtes appliqué par chaque fabrique PdfResponse invoquéeAucune ; les en-têtes ne sont pas configurables
Nom de fichier de téléchargementInjection d’en-tête, traversée de chemin dans Content-DispositionL’assainisseur de nom de fichier supprime les séparateurs, les caractères de contrôle et les octets nulsAucune ; l’assainisseur s’exécute toujours
Chemin de sortie de la tâche de file d’attenteÉcriture de fichier arbitraire via une charge utile sérialisée altéréeChemin validé dans handle() sur le workerDirige la sortie vers un chemin de stockage maîtrisé
Trafic HTTP sortant vers la TSAFalsification de requête côté serveur, altération en clairClient HTTP tenant compte des falsifications de requête ; HTTPS imposé sauf relâchement expliciteConserve tsa.allow_insecure_http = false ; épingle la SPKI
État partagé du workerFuite d’état entre requêtes dans les workers à longue durée de vieRegistre de polices verrouillé ; cache d’images borné ; document lié à la fabriqueDéfinis preload_fonts ; borne la mémoire au niveau du conteneur

Chaque fabrique PdfResponse applique un jeu fixe d’en-têtes :

  • 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

Ces valeurs sont des constantes dans PdfResponse. Elles ne sont pas configurables. La suite de tests du package vérifie chaque en-tête sur chaque méthode de fabrique, y compris les variantes en flux.

Le nom de fichier proposé au téléchargement passe par un assainisseur avant d’atteindre l’en-tête Content-Disposition. L’assainisseur retire les séparateurs de chemin, les caractères de contrôle et les octets nuls, et émet un paramètre RFC 5987 filename*= pour les noms non ASCII. Un nom de fichier vide devient document.pdf.

GeneratePdfJob sérialise une closure dans le transport de la file d’attente. Le chemin de sortie est validé dans handle() sur le worker, et non au moment de la répartition. La validation rejette :

  • les octets nuls dans le chemin,
  • les schémas de wrapper de flux (par exemple php://),
  • les segments de traversée de chemin ..,
  • tout chemin qui ne se termine pas par .pdf (insensible à la casse).

Chaque rejet lève InvalidArgumentException. La validation s’exécute au point de consommation. La charge utile sérialisée sur un transport Redis ou en base de données pourrait être altérée avant que le worker ne la lise. Dirige le chemin de sortie vers un répertoire de stockage maîtrisé ; ne le dérive pas d’une entrée de requête non validée.

Trafic HTTP sortant vers une autorité d’horodatage

Section intitulée « Trafic HTTP sortant vers une autorité d’horodatage »

Quand une autorité d’horodatage est configurée, le package lie un Psr\Http\Client\ClientInterface PSR-18. Un client PSR-18 envoie une requête PSR-7 et renvoie une réponse PSR-7 (PSR-18 §2). Le client lié enveloppe un client basé sur curl dans une couche tenant compte des falsifications de requête, qui impose HTTPS sauf si tsa.allow_insecure_http vaut explicitement true.

L’autorité d’horodatage est une fonctionnalité du niveau Premium. Le package Core documenté ici lie le client HTTP et le câblage du client d’horodatage ; la signature elle-même requiert nextpdf/premium. Cette page ne documente pas le comportement de la ligne de base PAdES au-delà de B-B ; les lignes de base supérieures sont hors périmètre.

Conseils d’exploitation pour l’autorité d’horodatage :

  1. Conserve tsa.allow_insecure_http défini sur false en production.
  2. Définis tsa.pinned_public_keys sur les empreintes SPKI SHA-256 en base64 du certificat de l’autorité d’horodatage (forme RFC 7469).
  3. Conserve tsa.warn_on_key_rotation défini sur true pour qu’une SPKI modifiée soit journalisée avant l’expiration du certificat épinglé.
  4. Ne définis tsa.url qu’à partir d’une configuration de confiance. Si un opérateur peut le définir depuis une interface d’administration, applique une politique de pare-feu de sortie ou de DNS pour réduire l’exposition aux falsifications de requête.

Utilise Psr\Log\LoggerInterface pour les diagnostics. Passe un contexte structuré, et non des chaînes interpolées. PSR-3 laisse l’échappement des espaces réservés à l’implémentation du logger et demande aux appelants de ne pas pré-échapper les valeurs de contexte (PSR-3 §1.2). Journalise la classe de l’exception, pas son message ni sa trace, afin de réduire le niveau de détail interne dans les journaux.

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
],
],
];
  • Le jeu d’en-têtes de réponse est figé. Les applications qui ont besoin d’une CSP différente doivent post-traiter la réponse après que la fabrique l’a renvoyée.
  • La validation du chemin s’exécute sur le worker. Un mauvais chemin passe dispatch() et n’échoue qu’au moment où la tâche s’exécute.
  • tsa.allow_insecure_http = true supprime l’imposition de HTTPS et affaiblit la confiance dans l’horodatage. Restreins-le au développement local.
  • Le registre de polices est verrouillé après le préchauffage ; une tentative d’enregistrer une police à l’exécution dans un worker à longue durée de vie est rejetée par conception.

Les contrôles de sécurité sont des opérations sur chaînes et tableaux à temps constant et n’ajoutent aucun coût mesurable par requête. Le coût d’exploitation dominant est l’analyse des polices à la première utilisation ; précharge les polices au démarrage du worker pour éviter la latence de la première requête.

Cette page est la référence du modèle de menace pour le package. Les contrôles décrits ici sont appliqués dans le code source et vérifiés par la suite de tests. La configuration de déploiement que l’opérateur doit fournir est indiquée dans le tableau du modèle de menace et dans les étapes relatives à l’autorité d’horodatage.

AffirmationSourceClausereference_id
Le client PSR-18 envoie une requête PSR-7, renvoie une réponse PSR-7Client HTTP PSR-18§2
L’appelant passe un contexte de journalisation structuré non échappéLogger PSR-3§1.2

L’épinglage SPKI RFC 7469 est mentionné comme la forme utilisée par la clé de configuration tsa.pinned_public_keys ; le package consomme les valeurs d’épingle fournies par l’opérateur et n’implémente pas la RFC lui-même.

La signature PAdES B-B et l’intégration de l’autorité d’horodatage requièrent nextpdf/premium. Il s’agit d’une fonctionnalité Enterprise optionnelle ; le package Core documenté ici n’a besoin d’aucune modification de code pour l’adopter. Voir https://nextpdf.dev/get-license/?intent=laravel-signing.

  • /integrations/laravel/configuration/ — chaque clé de TSA, de signature et de file d’attente
  • /integrations/laravel/production-usage/ — modèles d’injection de dépendances et de gestion des erreurs
  • /integrations/laravel/troubleshooting/ — pourquoi les vérifications de chemin rejettent l’entrée
  • /integrations/laravel/boot-and-discovery/ — durées de vie des liaisons dans les workers à longue durée de vie