Chiffrer un PDF et limiter les autorisations
Ce recipe chiffre un document avec le gestionnaire de sécurité standard AES-256. Il configure un mot de passe utilisateur (requis pour ouvrir le fichier) et un mot de passe propriétaire (accès complet), puis limite les opérations via un masque de bits d’autorisations. Ce recipe explicite volontairement la nature coopérative côté lecteur de ces autorisations : le chiffrement apporte la confidentialité, pas l’intégrité, et les bits d’autorisation ne sont respectés que par les logiciels coopérants. Ce recipe suit examples/22-protection.php.
Frontière de confiance (à garder en tête pour toute affirmation sur les autorisations). Le chiffrement PDF protège la confidentialité du contenu contre les acteurs dépourvus du mot de passe (ISO 32000-2 §7.6). Il ne protège pas l’intégrité : il ne détecte ni n’empêche aucune modification. L’entrée d’autorisations
Pest un ensemble de drapeaux non signé sur 32 bits que les lecteurs conformes sont invités à respecter ; ces drapeaux ne constituent pas un contrôle d’accès. Un outil non conforme, ou n’importe quel outil utilisé avec le mot de passe propriétaire, peut effectuer toute opération « refusée ». Ne décris pas un PDF chiffré comme « sécurisé », « inviolable » ou « protégé contre la copie ».
Installation
Section intitulée « Installation »composer require nextpdf/core:^3Active l’extension PHP openssl. Le chiffreur AES-256 l’utilise pour le chiffrement et la dérivation de clé.
Vue d’ensemble conceptuelle
Section intitulée « Vue d’ensemble conceptuelle »Le gestionnaire de sécurité standard est sélectionné via les codes V/R du dictionnaire de chiffrement (ISO 32000-2 §7.6). NextPDF, via son Aes256Encryptor, implémente le filtre de chiffrement AESV3 pour la révision 6 du gestionnaire de sécurité (V=5/R=6) : une clé de chiffrement de fichier aléatoire de 256 bits, une dérivation de clé par hachage itératif salé (Algorithme 2.B) et un chiffrement par objet AES-256-CBC avec un vecteur d’initialisation aléatoire. CBC est un mode de confidentialité (NIST SP 800-38A). Ses IV doivent être imprévisibles.
L’IV est régénéré pour chaque objet et à chaque exécution ; les octets bruts diffèrent donc d’une exécution à l’autre. Le profil de reproductibilité est donc structural. Avant de comparer deux exécutions, le harnais normalise l’IV de chiffrement, l’ordre des objets et le /ID du trailer. Ce profil est plus strict que celui d’un recipe qui omet le chiffrement.
Le masque de bits d’autorisations définit l’entrée P. Le bit 3 autorise l’impression, le bit 6 autorise l’annotation/form-fill ; la valeur correspond à la quantité non signée sur 32 bits documentée.
Surface d’API
Section intitulée « Surface d’API »NextPDF\Core\Concerns\HasSecurity (utilisé par Document) :
setEncryption(#[SensitiveParameter] string $userPassword, #[SensitiveParameter] string $ownerPassword = '', int $permissions = -1): static— configure le chiffrement AES-256 du gestionnaire standard.permissions = -1accorde tout. LorsqueownerPasswordest vide, le mot de passe utilisateur est réutilisé comme mot de passe propriétaire. Appelle cette méthode avantaddPage().getEncryptor(): ?Aes256Encryptor— le chiffreur configuré, ounull.useAesGcm(?bool $enabled = true): static— active l’AES-256-GCM d’ISO/TS 32003 ; lève une exception si l’OpenSSL/libsodium de l’hôte ne prend pas ce chiffrement en charge.
Les deux paramètres de mot de passe sont marqués #[SensitiveParameter], donc PHP les masque dans les traces de pile.
Bits d’autorisation (l’entrée P, bits de poids faible 3 à 6 couramment utilisés) :
| Bit | Valeur | Opération |
|---|---|---|
| 3 | 4 | Imprimer le document |
| 4 | 8 | Modifier le contenu du document |
| 5 | 16 | Copier / extraire le texte et les graphiques |
| 6 | 32 | Ajouter ou modifier des annotations et remplir les champs de formulaire |
Exemple de code — Démarrage rapide
Section intitulée « Exemple de code — Démarrage rapide »<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->setTitle('Confidential Memo');
// Grant printing only (bit 3 = 4). MUST run before addPage().$doc->setEncryption( userPassword: 'open-me', ownerPassword: 'owner-secret', permissions: 4,);
$doc->addPage();$doc->setFont('helvetica', '', 12);$doc->cell(0, 10, 'Encrypted with AES-256; printing allowed only.', newLine: true);
$doc->save(__DIR__ . '/confidential.pdf');echo "Wrote confidential.pdf\n";Exemple de code — Production
Section intitulée « Exemple de code — Production »L’exemple complet ci-dessous reflète examples/22-protection.php et écrit dans NEXTPDF_COOKBOOK_OUTPUT pour le harnais.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$userPassword = 'demo';$ownerPassword = 'admin';
// Grant ONLY printing (bit 3 = 4); deny copy/modify/annotate.$permissions = 4;
$doc = Document::createStandalone();$doc->setTitle('Encrypted Document — Restricted Permissions');$doc->setAuthor('NextPDF Example');
// setEncryption() MUST be called before addPage().$doc->setEncryption( userPassword: $userPassword, ownerPassword: $ownerPassword, permissions: $permissions,);
$doc->addPage();$doc->setFont('helvetica', 'B', 20);$doc->cell(0, 14, 'Encrypted PDF Document', newLine: true);$doc->ln(8);
$doc->setFont('helvetica', '', 11);$doc->multiCell(0, 7, 'This document is protected with AES-256 encryption ' . '(standard security handler, revision 6). The user password is required ' . 'to open it; the owner password grants full access. The permission ' . 'bits below are honoured by conforming readers only.');$doc->ln(5);
$permissionTable = [ ['Bit 3 (4)', 'Printing', 'ALLOWED'], ['Bit 4 (8)', 'Content modification', 'DENIED'], ['Bit 5 (16)', 'Text copying / extraction', 'DENIED'], ['Bit 6 (32)', 'Annotations / form fields', 'DENIED'],];$doc->setFont('helvetica', 'B', 10);$doc->cell(30, 7, 'Flag');$doc->cell(60, 7, 'Operation');$doc->cell(0, 7, 'Status', newLine: true);foreach ($permissionTable as [$bit, $operation, $status]) { $doc->setFont('courier', '', 9); $doc->cell(30, 7, $bit); $doc->setFont('helvetica', '', 10); $doc->cell(60, 7, $operation); $doc->setFont('helvetica', 'B', 10); $doc->cell(0, 7, $status, newLine: true);}
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');$doc->save($out !== false ? $out : __DIR__ . '/encrypted.pdf');
echo "Wrote encrypted PDF (AES-256, printing only)\n";Sortie attendue :
Wrote encrypted PDF (AES-256, printing only)L’ouverture du fichier demande un mot de passe. Le mot de passe utilisateur l’ouvre avec le jeu d’autorisations restreint. Le mot de passe propriétaire l’ouvre avec un accès complet.
Cas limites & pièges
Section intitulée « Cas limites & pièges »- Ordre des appels.
setEncryption()aprèsaddPage()ne chiffre pas rétroactivement le contenu précédent. Configure toujours le chiffrement en premier ; le moteur chiffre le corps de chaque objet au moment où il est écrit. - Mot de passe propriétaire par défaut. Un mot de passe propriétaire vide amène le moteur à réutiliser le mot de passe utilisateur comme mot de passe propriétaire — il n’y a alors plus vraiment de rôle privilégié. Définis des mots de passe distincts lorsque les deux rôles doivent rester séparés.
- Les autorisations ont une sémantique indicative. Les bits ne sont respectés que par les lecteurs conformes. Ils ne sont pas imposés cryptographiquement : un outil non conforme, ou n’importe quel outil utilisé avec le mot de passe propriétaire, peut effectuer les opérations restreintes. Traite les autorisations comme un signal de politique adressé aux logiciels coopérants, jamais comme un contrôle d’accès capable de résister à une partie déterminée.
- Aucune garantie d’intégrité. Le chiffrement apporte de la confidentialité, pas de l’intégrité. Un attaquant dépourvu du mot de passe ne peut pas lire le contenu, mais le format lui-même ne détecte pas la falsification. Pour une protection de l’intégrité, un mécanisme distinct (une signature numérique, ou le MAC de document d’ISO/TS 32004) est requis.
- Conflit avec PDF/A. PDF/A interdit la clé de trailer
Encrypt. AppelersetEncryption()sur un document PDF/A, dans un ordre ou dans l’autre, lève une exception d’incompatibilité. - Activation d’AES-256-GCM.
useAesGcm()sélectionne le chiffrement en masse GCM d’ISO/TS 32003 quand l’OpenSSL ou la libsodium de l’hôte le propose ; sinon, il lèveInvalidConfigException. Il est incompatible avec PDF/A pour la même raison. - Le chiffrement à clé publique n’est pas encore branché.
setPublicKeyEncryption()fige la surface d’API, maissave()lève une exception jusqu’à ce que le branchement de l’écrivain soit disponible (un défaut connu) ; ne l’utilise pas en production sur le cœur.
Performance
Section intitulée « Performance »La dérivation de clé effectue le hachage itéré de l’Algorithme 2.B une fois par document. L’AES-256-CBC par objet est linéaire par rapport à la taille du corps de l’objet. Pour les documents typiques, le coût reste largement dans le budget de 1500 ms / 64 Mo. Les très grands documents subissent un coût de débit AES par objet. GCM avec AES-NI est plus rapide sur les hôtes qui en sont capables.
Notes de sécurité
Section intitulée « Notes de sécurité »- Confidentialité uniquement. Pour rappeler la frontière de confiance : le chiffrement empêche les acteurs dépourvus du mot de passe d’accéder au contenu ; il ne prouve pas que le fichier est inaltéré, et les bits d’autorisation reposent sur la coopération du lecteur.
- La robustesse du mot de passe t’incombe. Le gestionnaire ne peut pas être plus robuste que les mots de passe. Un mot de passe utilisateur faible peut être attaqué par force brute hors ligne une fois le fichier obtenu ; le format ne peut pas limiter la fréquence des tentatives.
- Le mot de passe propriétaire est une clé primaire. Quiconque détient le mot de passe propriétaire contourne chaque restriction. Traite-le comme un identifiant root ; ne le livre jamais avec le document et ne le journalise jamais.
#[SensitiveParameter]est une défense en profondeur. Il masque les mots de passe des traces de pile PHP, mais tu dois quand même les tenir à l’écart de tes propres journaux, messages d’exception et rapports de plantage.
Résidence des données & mesures d’atténuation des PII
Section intitulée « Résidence des données & mesures d’atténuation des PII »La bibliothèque effectue le chiffrement dans le processus. Elle ne transmet ni le document ni les mots de passe où que ce soit. Aucun mot de passe, clé ou octet de document n’est écrit sur le disque par le moteur, hormis la sortie chiffrée que tu enregistres. L’emplacement du fichier de sortie et la conservation des mots de passe sont des préoccupations de déploiement qui incombent à l’intégrateur. La bibliothèque n’offre aucune garantie de résidence. Si le document en clair contient des données personnelles, ces données ne sont protégées qu’à hauteur du mot de passe le plus faible et de la réserve sur la coopération du lecteur évoquée ci-dessus. Le chiffrement ne remplace pas la minimisation des PII que tu places dans le document.
Télémétrie sûre & nettoyage des journaux
Section intitulée « Télémétrie sûre & nettoyage des journaux »Le chiffrement émet un EncryptionAppliedEvent qui ne transporte que le nom de l’algorithme (AES-256) et trois booléens indiquant si print/copy/modify sont autorisés — aucun mot de passe, clé, sel ou IV n’est jamais ajouté à l’événement (src/Event/Security/EncryptionAppliedEvent.php). Le chemin OpenTelemetry achemine les attributs de span via un nettoyeur fondé sur une liste d’autorisation (src/Telemetry/AttributeSanitizer.php) qui rejette inconditionnellement les mots de passe et les chemins de fichiers ; seules les clés autorisées avec des valeurs scalaires sont conservées. N’ajoute pas de mot de passe ni de matériel de clé aux spans, aux journaux ou aux messages d’exception dans ton propre code d’intégration — les marqueurs #[SensitiveParameter] protègent les traces de pile, mais pas les chaînes que tu construis toi-même.
Modèle de menace
Section intitulée « Modèle de menace »Dans le périmètre : un adversaire qui obtient le fichier chiffré mais pas les mots de passe — il ne peut pas lire le contenu (sous réserve de la robustesse du mot de passe) et le fichier ne laisse pas fuiter de texte en clair. Hors périmètre : un adversaire qui détient le mot de passe utilisateur ou propriétaire ; un lecteur non conforme qui ignore les bits d’autorisation ; la force brute hors ligne d’un mot de passe faible ; la détection de la falsification (le chiffrement apporte la confidentialité, pas l’intégrité) ; les canaux auxiliaires dans la build OpenSSL de l’hôte ; et la garde des clés, qui relève entièrement de la responsabilité de l’intégrateur. Le fait de documenter ces menaces n’affirme pas l’absence de vulnérabilités.
Comportement en mode FIPS
Section intitulée « Comportement en mode FIPS »Les primitives cryptographiques sont fournies par la build OpenSSL de l’hôte ; la posture FIPS est donc une propriété de l’hôte, pas un réglage de la bibliothèque. CryptoCapabilities::detectFipsMode() renvoie un FipsModeDetection à trois états (src/Security/FipsModeDetection.php) : FIPS_ACTIVE, FIPS_ABSENT, ou INDETERMINATE. L’extension openssl de PHP n’expose aucune liaison vers le modèle de fournisseur d’OpenSSL 3 ; la détection se fait donc au mieux. INDETERMINATE est traité comme « FIPS non prouvé » (sûr par défaut), distinguable dans une télémétrie exploitable par l’opérateur. NextPDF ne revendique pas la validation FIPS 140 ; s’exécuter sur un OpenSSL validé FIPS relève de la responsabilité de l’opérateur, et le résultat de la détection est indicatif.
Conformité
Section intitulée « Conformité »| Énoncé | Spécification | Clause | reference_id |
|---|---|---|---|
Le code V du dictionnaire de chiffrement sélectionne l’algorithme de chiffrement. | ISO 32000-2 | §7.6 | |
La méthode de filtre de chiffrement AESV3 est nommée par l’entrée CFM. | ISO 32000-2 | §7.6 | |
L’entrée P est une quantité d’autorisations d’accès non signée sur 32 bits. | ISO 32000-2 | §7.6 | |
| Le bit d’autorisation 3 contrôle l’impression. | ISO 32000-2 | §7.6 | |
| Le bit d’autorisation 6 contrôle l’annotation / le remplissage de formulaire. | ISO 32000-2 | §7.6 | |
| Le chiffrement protège le contenu contre les accès non autorisés (confidentialité). | ISO 32000-2 | §7.6 | |
| La dérivation de clé de la révision 6 utilise un hachage itératif salé (Algorithme 2.B). | ISO 32000-2 | §7.6 | |
| CBC est un mode de confidentialité (pas un mode d’intégrité). | NIST SP 800-38A | §6.2 | |
| Les vecteurs d’initialisation CBC doivent être imprévisibles. | NIST SP 800-38A | Annexe C |
NextPDF implémente les clauses citées ; il n’affirme pas de conformité ISO 32000-2 globale, de validation FIPS 140, ni aucune garantie légale ou contractuelle de confidentialité. « La prise en charge du gestionnaire de sécurité standard » n’est pas une certification de sécurité dans ton déploiement — cela dépend de la garde des mots de passe et de la politique du vérificateur, hors de la bibliothèque.