Assainir des PDF non fiables : désarmement et reconstruction de contenu
Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4
Un PDF qui arrive du monde extérieur est autant du code qu’un document. Il peut transporter du JavaScript, une action Launch, un exécutable incorporé, et une structure conçue pour faire trébucher un analyseur. Le Content Disarm & Reconstruction (CDR) traite ce fichier comme non fiable, ne garde que ce qui est sûr, et reconstruit un PDF propre à partir des survivants.
Cette page explique comment le CdrEngine de NextPDF Enterprise fait cela — et la
seule chose honnête que chaque fournisseur de CDR devrait dire à voix haute : le
fichier reconstruit est une projection de sécurité de l’original, pas une copie
fidèle de celui-ci.
Pourquoi c’est important
Section intitulée « Pourquoi c’est important »Les parties dangereuses d’un PDF ne sont pas exotiques. Ce sont des fonctionnalités standard. La spécification définit tout un catalogue d’actions — ce qui se passe à l’ouverture d’un document, à l’affichage d’une page, au changement d’un champ — et ce catalogue inclut les actions JavaScript et Launch (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4). Un lecteur qui honore le format les exécutera volontiers. C’est le point d’appui de l’attaquant : un fichier qui est parfaitement valide et parfaitement malveillant en même temps.
Filtrer par extension de fichier ne sert à rien ici. La menace est à l’intérieur d’un PDF bien formé, de sorte que la seule vraie défense est de l’ouvrir, de le comprendre, et de retirer la machinerie active avant qu’elle n’atteigne jamais un moteur de rendu. C’est la posture de validation des entrées que décrivent les recommandations d’OWASP sur le téléversement de fichiers et la validation des entrées : ne jamais faire confiance aux octets, et préférer reconstruire un artefact connu comme bon plutôt que de scruter un fichier hostile à la recherche de signatures connues comme mauvaises.
La version courte
Section intitulée « La version courte »- Le CDR suppose que l’entrée est hostile et produit un nouveau fichier plutôt que de rapiécer l’ancien.
CdrEngine::sanitize()exécute six phases : analyse, contrôle d’admission, détection de menaces, filtrage, nettoyage des références, reconstruction.- Il retourne un
CdrResultqui t’indique ce qui a été retiré, si le document a même été admis, et — sinon — pourquoi il a été rejeté. - La sortie est une projection de sécurité. Ce n’est explicitement pas une copie à valeur de preuve, une correspondance de hachage, ni un artefact d’archivage. C’est un contrat, pas une réserve.
- Ceci est réservé à Enterprise. Le core de NextPDF n’effectue pas de CDR.
L’approche de NextPDF
Section intitulée « L’approche de NextPDF »La documentation propre du moteur énonce la ligne rouge de conception en trois mots :
Security Projection Layer. sanitize() est une transformation destructive et non
réversible. Elle a le droit de jeter des octets. Ce qu’elle n’a pas le droit de faire,
c’est de prétendre que le résultat est le même document.
La chaîne est délibérément ordonnée. Chaque phase resserre la confiance avant que la phase suivante n’agisse dessus.
- ParsePdfReader builds the object graph from the raw bytes. A parse failure is a rejection, not a best-effort guess.
- Admission controlResource limits are checked first — object count, page count, decoded-stream size, per-stream inflation ratio. Over-budget input is rejected, never sanitised.
- DetectThreatDetector walks the graph and records each dangerous feature as a DetectedThreat with its object number and type.
- FilterObjects are partitioned into safe and removed. Catalog-level keys are stripped in place so the document catalog itself survives.
- Reference scrubPointers to removed objects are cleaned so the rebuilt file has no dangling references.
- RebuildCdrRebuilder serialises only the safe objects into a new PDF. The output is fresh structure, not an edited original.
L’admission avant le désarmement. Avant qu’une seule menace ne soit retirée, le
moteur demande si le document vaut même la peine d’être traité. CdrPolicy porte les
limites — maxObjects, maxPageCount, maxDecodedStreamBytes, et un
maxInflationRatio qui défend contre les bombes de décompression. Un document qui les
dépasse est rejeté, et le CdrResult le dit avec admitted: false et un
rejectionReason. Cette distinction est porteuse : « nous l’avons nettoyé » et « nous
l’avons refusé » sont des résultats différents, et le type de résultat les sépare pour
que ton signalement d’erreurs puisse en faire autant.
Les menaces sont nommées, pas devinées. ThreatDetector::detect() associe chaque
construction dangereuse à un ThreatType — JavaScript, LaunchAction,
OpenAction, AdditionalActions, RemoteGoTo, SubmitForm, ImportData,
EmbeddedFiles, RichMedia, NamedJavaScript, et d’autres — chacun lié à une
fonctionnalité PDF précise. Un objet qui ne peut pas être analysé du tout devient son
propre type de menace, UnparseableObject, car un objet que l’assainisseur ne peut pas
lire est un objet dont il ne peut pas se porter garant. Chaque trouvaille est un
DetectedThreat portant le numéro de l’objet fautif, de sorte que la suppression est
précise.
La suppression protège le document, pas seulement la charge utile. Certaines clés
dangereuses vivent sur le catalogue du document (la Root) — une /OpenAction qui se
déclenche à l’ouverture, par exemple. Supprimer tout l’objet Root pour tuer une seule
clé détruirait le catalogue et casserait silencieusement le fichier. Le moteur les gère
plutôt comme des retraits de clé sur place sur le catalogue, et comme garde-fou à
échec fermé il refuse d’émettre un document reconstruit qui aurait perdu sa /Root
tout court. Un assainisseur qui produit un fichier structurellement cassé tout en
rapportant un succès est exactement le mode de défaillance que ce garde-fou existe pour
empêcher.
Exemple pratique
Section intitulée « Exemple pratique »La forme ci-dessous est le vrai point d’entrée. Tu remets au moteur des octets bruts et
une politique ; tu récupères un CdrResult qui est honnête sur ce qui s’est passé.
<?php
declare(strict_types=1);
use NextPDF\Enterprise\Security\Cdr\CdrEngine;use NextPDF\Enterprise\Security\Cdr\CdrPolicy;
$engine = new CdrEngine();
// Standard policy removes the known active threats — JavaScript, Launch// actions, remote go-to, form submit/import, and the rest — while leaving// the lossy opt-in "Strip*" cases off by default.$result = $engine->sanitize($untrustedPdfBytes, CdrPolicy::standard());
if (!$result->admitted) { // Rejected by admission control (e.g. object/page limit, zip bomb). // This is NOT a sanitised document. Do not serve it; report the reason. throw new \RuntimeException($result->rejectionReason);}
if ($result->hadThreats()) { // The disarmed bytes are safe to render. Each removed threat carries its // type and object number for your audit log — never silently. foreach ($result->removedThreats as $threat) { error_log(\sprintf( 'CDR removed %s in object %d', $threat->type->value, $threat->objectNumber, )); }}
$cleanBytes = $result->sanitizedPdf; // The security projection. Not the original.Il n’existe aucun chemin où ce code rend discrètement un fichier encore dangereux. Soit
le document est admis et désarmé, soit il est rejeté avec une raison énoncée. La liste
removedThreats signifie que le désarmement est auditable, pas magique.
Idée fausse courante
Section intitulée « Idée fausse courante »« Le CDR n’est que du caviardage avec des étapes en plus. »
Ce n’est pas le cas, et confondre les deux est dangereux. Le caviardage supprime de l’information — des noms, des numéros de compte, le contenu qu’un humain ne doit pas voir. Le CDR supprime de la capacité — le JavaScript, l’action Launch, la charge utile incorporée qu’une machine ne doit pas exécuter. Ils ont des critères de succès opposés. Un caviardage est correct lorsque le contenu sensible a disparu et que le reste est préservé verbatim. Un désarmement est correct lorsque la menace a disparu, et il est parfaitement disposé à altérer une structure bénigne pour y parvenir. Utilise l’outil qui correspond à ton intention ; ne te tourne pas vers l’un en attendant les garanties de l’autre.
Une seconde idée fausse est qu’une reconstruction propre prouve que l’original était propre. Elle ne prouve rien sur l’original. Elle prouve seulement que la sortie ne contient aucune menace détectée. L’entrée a pu être une arme ; le travail du CDR est de s’assurer que ce que tu transmets en aval n’en est pas une.
Limites et frontières
Section intitulée « Limites et frontières »C’est la partie que les brochures sautent, alors nous allons la dire clairement.
- La sortie est une projection de sécurité, pas une copie à valeur de preuve. Le
code source de
CdrEngineporte ceci comme une ligne rouge architecturale. Le PDF assaini ne doit pas être utilisé pour la conservation de preuves juridiques, pour une comparaison de hachage avec l’original, ni comme copie d’archivage. La transformation est destructive et non réversible par conception. - La détection a une portée. Le CDR retire les menaces qu’il sait nommer. C’est une couche forte et auditable dans une pile de défense en profondeur — pas une garantie qu’un fichier est exempt de toute technique future possible. Garde-le derrière les mêmes validations de téléversement, vérifications de type de contenu et manipulation au moindre privilège que décrivent les recommandations d’OWASP sur le téléversement de fichiers.
- Certaines politiques sont intentionnellement avec perte. Les cas
Strip*à activer retirent les fichiers incorporés, les signatures, les champs de formulaire, les calques et les médias 3D. Ils sont puissants et ils supprimeront du contenu légitime — une charge utile de facture ZUGFeRD/Factur-X, par exemple. Ils sont désactivés par défaut exactement pour cette raison. Active-les en connaissance de cause. - Les signatures ne survivent pas à une reconstruction. Reconstruire le fichier change ses octets, de sorte que toute signature numérique d’origine ne correspond plus à sa plage d’octets. Un document désarmé n’est pas signé par rapport à la source. Si tu as besoin d’un artefact signé, signe la sortie propre comme un nouvel acte.
| Edition | Availability |
|---|---|
| Core | Not available. NextPDF core does not perform CDR. It parses, renders, and writes PDFs; it does not threat-detect or rebuild untrusted input. |
| Pro | Not available in the Pro edition. |
| Enterprise | Available via |
Documents liés
Section intitulée « Documents liés »- Comment fonctionne réellement le chiffrement PDF — l’autre moitié de la manipulation de PDF sensibles : protéger le contenu par rapport à supprimer la capacité.
- Les erreurs comme fonctionnalité — la
philosophie d’échec fermé qu’incarnent les rejets du contrôle d’admission du CDR et le
garde-fou
/Root. - Une API qui refuse de deviner — pourquoi un type de résultat qui distingue nettoyé de rejeté vaut mieux qu’un effort silencieux au mieux.
Glossaire
Section intitulée « Glossaire »- CDR (Content Disarm & Reconstruction) — une stratégie d’assainissement qui analyse un fichier non fiable, retire les composants actifs ou dangereux, et reconstruit un fichier propre à partir du reste sûr, plutôt que de scruter à la recherche de signatures connues comme mauvaises.
- Projection de sécurité — une sortie assainie qui préserve assez de la source pour être utile tout en garantissant la suppression des menaces. Elle n’est délibérément pas fidèle aux octets et n’est pas adaptée à la preuve, au hachage ou à l’archivage.
- Contrôle d’admission — la porte de pré-assainissement qui rejette les documents dépassant les limites de ressources (nombre d’objets, nombre de pages, taille de flux décodé, ratio d’inflation) avant que tout travail de désarmement ne commence.
- Action — une construction PDF qui fait se produire quelque chose sur un déclencheur tel que l’ouverture de document ou le changement d’un champ ; le catalogue des types d’action (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4) inclut les actions JavaScript et Launch, la surface de menace canonique du CDR.
- Référence pendante — un pointeur vers un objet qui n’existe plus après filtrage. La phase de nettoyage des références les supprime pour que le fichier reconstruit reste structurellement cohérent.