Skip to content

Meilleures pratiques de sécurité

Ce guide fournit des recommandations de sécurité actionnables pour déployer TCPDF-Next dans des environnements de production. Suivre ces pratiques garantit que votre pipeline de génération PDF répond aux standards de sécurité d'entreprise.

Validation d'entrée (sanitization HTML avant writeHtml)

Lors de la génération de PDF depuis HTML fourni par l'utilisateur, sanitisez toujours l'entrée avant de la passer au moteur de rendu HTML. Le HtmlRenderer de TCPDF-Next analyse et rend fidèlement le HTML, ce qui signifie que le balisage malveillant peut être exploité s'il n'est pas sanitisé.

php
use YeeeFang\TcpdfNext\Html\HtmlRenderer;

// DANGEREUX : Ne jamais passer d'entrée utilisateur brute directement
// $renderer->writeHtml($userInput);

// SÛR : Sanitiser d'abord avec une bibliothèque dédiée
$clean = \HTMLPurifier::getInstance()->purify($userInput);
$renderer->writeHtml($clean);

Règles clés :

  • Supprimer les balises <script>, <iframe>, <object>, <embed> et <link> avant rendu.
  • Retirer les schémas URI javascript: et data: des attributs href et src.
  • Limiter les propriétés CSS autorisées à celles nécessaires pour la mise en page (pas de position: fixed, pas de url() dans valeurs CSS).
  • Valider l'encodage de caractères — assurer que l'entrée est UTF-8 valide avant passage au moteur de rendu.

Gestion des certificats (stockage sécurisé et rotation)

Hiérarchie de stockage

MéthodeNiveau de sécuritéCas d'usage
Module de sécurité matériel (HSM)Le plus élevéEnvironnements de production, industries réglementées
Cloud KMS (AWS KMS, Azure Key Vault, GCP KMS)ÉlevéDéploiements cloud natifs
Fichier PKCS#12 avec phrase de passe forteMoyenPetits déploiements
Fichier PEM (chiffré)Moyen-BasDéveloppement, tests
Fichier PEM (non chiffré)Le plus basJamais en production

Politique de rotation

  • Renouveler les certificats au moins 30 jours avant expiration.
  • Surveiller l'expiration avec alertes automatisées aux seuils 30, 14, 7 et 1 jour.
  • Révoquer immédiatement les certificats compromis via l'AC émettrice.
  • Maintenir un journal d'audit signé de toutes les opérations du cycle de vie des certificats.
php
use YeeeFang\TcpdfNext\Certificate\CertificateStore;

$store = new CertificateStore();
$store->loadFromDirectory('/etc/tcpdf-next/certs/', '*.pem');

$activeCert = $store->getActiveCertificate('document-signing');

if ($activeCert->getExpirationDate() < new \DateTimeImmutable('+30 days')) {
    $logger->warning('Signing certificate expires soon', [
        'subject' => $activeCert->getSubject(),
        'expires' => $activeCert->getExpirationDate()->format('Y-m-d'),
    ]);
}

DANGER

Ne jamais stocker de clés privées dans dépôts de code source, fichiers non chiffrés sur systèmes de fichiers partagés, colonnes de base de données sans chiffrement au repos, ou fichiers journaux.

Gestion des mots de passe (SASLprep et mots de passe forts)

Lors de la définition de mots de passe de chiffrement PDF, appliquez la normalisation Unicode via SASLprep (RFC 4013) pour assurer un traitement cohérent des mots de passe sur toutes les plateformes :

php
// TCPDF-Next applique automatiquement SASLprep aux mots de passe
$pdf->setEncryption()
    ->setAlgorithm(EncryptionAlgorithm::AES256)
    ->setUserPassword('pässwörd-with-ünïcöde')  // SASLprep normalisé en interne
    ->setOwnerPassword($strongOwnerPassword)
    ->apply();

Recommandations de politique de mots de passe :

  • Minimum 12 caractères pour mots de passe utilisateur, 20 caractères pour mots de passe propriétaire.
  • Utiliser un générateur aléatoire cryptographiquement sécurisé pour mots de passe propriétaire (random_bytes()).
  • Ne jamais coder en dur les mots de passe dans le code source — charger depuis variables d'environnement ou gestionnaires de secrets.
  • Effacer les mots de passe de la mémoire après utilisation avec sodium_memzero().

Prévention SSRF (validation URL pour images, TSA, OCSP)

TCPDF-Next bloque SSRF par défaut, mais vous devez configurer des listes blanches pour ressources externes légitimes :

php
use YeeeFang\TcpdfNext\Security\NetworkPolicy;

$networkPolicy = NetworkPolicy::create()
    ->denyPrivateNetworks()     // Bloquer 10.x, 172.16.x, 192.168.x
    ->denyLoopback()            // Bloquer 127.0.0.1
    ->denyLinkLocal()           // Bloquer 169.254.x
    ->allowDomain('cdn.yourcompany.com')       // Images
    ->allowDomain('timestamp.digicert.com')    // TSA
    ->allowDomain('ocsp.digicert.com')         // OCSP
    ->setMaxRedirects(3)
    ->setRequestTimeout(10);

$pdf = PdfDocument::create()
    ->setNetworkPolicy($networkPolicy)
    ->build();

Liste de contrôle :

  • Valider toutes les URL avant récupération (schéma, hôte, port).
  • Lister explicitement en blanc les domaines de répondeurs TSA et OCSP.
  • Bloquer file://, gopher://, ftp:// et autres schémas non-HTTP(S).
  • Journaliser toutes les requêtes bloquées pour surveillance de sécurité.

Validation de chemin de fichier (prévenir traversée de chemin)

Lors de l'acceptation de chemins de fichiers fournis par l'utilisateur (ex : pour fichiers de polices, images ou destinations de sortie) :

php
use YeeeFang\TcpdfNext\Security\ResourcePolicy;

$resourcePolicy = ResourcePolicy::strict()
    ->allowLocalDirectory('/app/public/assets/')
    ->allowLocalDirectory('/app/storage/fonts/')
    ->denyAllRemote();

$pdf = PdfDocument::create()
    ->setResourcePolicy($resourcePolicy)
    ->build();

Règles :

  • Ne jamais concaténer l'entrée utilisateur directement dans chemins de fichiers.
  • Résoudre les chemins en forme canonique absolue et valider contre répertoires autorisés.
  • Rejeter les chemins contenant .., octets nuls ou caractères non imprimables.
  • Utiliser ResourcePolicy::strict() en production — refuse tout accès par défaut.

Sécurité de déploiement (Docker et permissions de fichiers)

Configuration Docker

dockerfile
FROM php:8.5-fpm-alpine

# Exécuter comme utilisateur non-root
RUN addgroup -S tcpdf && adduser -S tcpdf -G tcpdf
USER tcpdf

# Désactiver fonctions PHP dangereuses
RUN echo "disable_functions = exec,passthru,shell_exec,system,proc_open,popen" \
    >> /usr/local/etc/php/conf.d/security.ini

# Système de fichiers en lecture seule (monter volumes modifiables explicitement)
# docker run --read-only --tmpfs /tmp ...

Permissions de fichiers

bash
# Répertoire certificats : lisible uniquement par utilisateur serveur web
chown -R www-data:www-data /etc/tcpdf-next/certs/
chmod 700 /etc/tcpdf-next/certs/
chmod 600 /etc/tcpdf-next/certs/*.p12
chmod 600 /etc/tcpdf-next/certs/*.pem

# Répertoire sortie : modifiable uniquement par utilisateur serveur web
chown -R www-data:www-data /var/lib/tcpdf-next/output/
chmod 700 /var/lib/tcpdf-next/output/

# Répertoire temporaire : modifiable, non lisible par tous
chown -R www-data:www-data /tmp/tcpdf-next/
chmod 700 /tmp/tcpdf-next/

Content Security Policy pour PDF affichés dans navigateur

Lors de la diffusion de PDF inline dans le navigateur, définissez des en-têtes HTTP appropriés pour prévenir les attaques d'intégration :

php
return response($pdf->toString(), 200, [
    'Content-Type' => 'application/pdf',
    'Content-Disposition' => 'inline; filename="document.pdf"',
    'Content-Security-Policy' => "default-src 'none'; plugin-types application/pdf",
    'X-Content-Type-Options' => 'nosniff',
    'X-Frame-Options' => 'DENY',
    'Cache-Control' => 'no-store, no-cache, must-revalidate',
]);

Pour les PDF contenant des données sensibles, préférez Content-Disposition: attachment pour forcer le téléchargement au lieu du rendu dans le navigateur.

Recommandations de journalisation d'audit

Configurez une journalisation d'audit complète pour toutes les opérations PDF sensibles en termes de sécurité :

php
use YeeeFang\TcpdfNext\Security\AuditLogger;

AuditLogger::configure([
    'channel' => 'tcpdf-security',
    'log_signing' => true,
    'log_encryption' => true,
    'log_validation' => true,
    'log_key_access' => true,
    'log_tsa_requests' => true,
    'log_resource_access' => true,   // Journaliser chargement image/police
    'log_blocked_requests' => true,  // Journaliser blocages SSRF
    'redact_sensitive' => true,      // Rédiger mots de passe/clés des journaux
]);

Surveiller pour :

  • Tentatives de validation de signature échouées (possible altération de document).
  • Avertissements d'expiration de certificat.
  • Échecs de communication TSA.
  • Volume de signature inhabituel (possible compromission de clé).
  • Tentatives SSRF bloquées (possible sondage d'attaque).
  • Chargement de ressources depuis chemins inattendus.

Principe du moindre privilège pour permissions PDF

Lors de la définition des permissions de document PDF, n'accorder que l'accès minimum requis :

php
use YeeeFang\TcpdfNext\Encryption\Permissions;

// RESTRICTIF : Document en lecture seule
$pdf->setEncryption()
    ->setPermissions(Permissions::ACCESSIBILITY)  // Accès lecteur d'écran uniquement
    ->setUserPassword('reader')
    ->setOwnerPassword($strongOwnerPassword)
    ->apply();

// MODÉRÉ : Document imprimable
$pdf->setEncryption()
    ->setPermissions(
        Permissions::PRINT_HIGH_QUALITY
        | Permissions::ACCESSIBILITY
    )
    ->apply();

// ÉVITER : Accorder toutes permissions défait l'objectif du chiffrement
// Permissions::ALL est disponible mais devrait rarement être utilisé

Lignes directrices de permissions :

  • Ne jamais accorder MODIFY_CONTENTS sauf si le destinataire doit éditer le document.
  • Toujours accorder ACCESSIBILITY pour compatibilité lecteur d'écran (exigence légale dans nombreuses juridictions).
  • Utiliser PRINT_HIGH_QUALITY au lieu de PRINT_LOW_QUALITY sauf si vous avez une raison spécifique.
  • Documenter la justification de chaque permission accordée dans votre code.

Lecture complémentaire

Distribué sous licence LGPL-3.0-or-later.