Sanitizzare PDF non fidati: content disarm and reconstruction
Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4
In sintesi
Sezione intitolata “In sintesi”Un PDF che arriva dal mondo esterno è codice tanto quanto è un documento. Può portare JavaScript, una Launch action, un eseguibile incorporato, e una struttura costruita per far inciampare un parser. Il Content Disarm & Reconstruction (CDR) tratta quel file come non fidato, mantiene solo ciò che è sicuro, e ricostruisce un PDF pulito dai sopravvissuti.
Questa pagina spiega come il CdrEngine di NextPDF Enterprise lo fa — e l’unica
cosa onesta che ogni fornitore di CDR dovrebbe dire ad alta voce: il file
ricostruito è una proiezione di sicurezza dell’originale, non una sua copia
fedele.
Perché è importante
Sezione intitolata “Perché è importante”Le parti pericolose di un PDF non sono esotiche. Sono funzionalità standard. La specifica definisce un intero catalogo di action — cosa succede quando un documento si apre, quando una pagina viene mostrata, quando un campo cambia — e quel catalogo include JavaScript e Launch action (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4). Un lettore che onora il formato le eseguirà volentieri. Questo è l’appiglio dell’attaccante: un file che è perfettamente valido e perfettamente malevolo allo stesso tempo.
Filtrare per estensione del file non serve a nulla qui. La minaccia è dentro un PDF ben formato, quindi l’unica vera difesa è aprirlo, comprenderlo, e spogliarlo della macchineria attiva prima che raggiunga mai un renderer. Questa è la postura di validazione degli input che descrive la guida OWASP su file-upload e validazione degli input: non fidarsi mai dei byte, e preferire ricostruire un artefatto noto-buono piuttosto che scandire uno ostile alla ricerca di firme note-cattive.
In breve
Sezione intitolata “In breve”- Il CDR presuppone che l’input sia ostile e produce un file nuovo anziché rattoppare quello vecchio.
CdrEngine::sanitize()esegue sei fasi: parse, controllo di ammissione, rilevamento delle minacce, filter, scrub dei riferimenti, rebuild.- Restituisce un
CdrResultche dice cosa è stato rimosso, se il documento sia stato persino ammesso, e — se no — perché sia stato respinto. - L’output è una proiezione di sicurezza. Non è esplicitamente non una copia probatoria, una corrispondenza di hash, o un artefatto di archiviazione. Questo è un contratto, non un’avvertenza.
- Questo è solo Enterprise. Il core di NextPDF non esegue CDR.
Come NextPDF lo affronta
Sezione intitolata “Come NextPDF lo affronta”La documentazione stessa del motore dichiara la linea rossa di progettazione in
tre parole: Security Projection Layer. sanitize() è una trasformazione
distruttiva e non reversibile. Le è permesso buttare via byte. Ciò che non le è
permesso fare è fingere che il risultato sia lo stesso documento.
La pipeline è deliberatamente ordinata. Ogni fase restringe la fiducia prima che la fase successiva agisca su di essa.
- 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.
Ammissione prima del disarm. Prima che una singola minaccia sia rimossa, il
motore chiede se il documento valga persino la pena di essere elaborato.
CdrPolicy porta i limiti — maxObjects, maxPageCount,
maxDecodedStreamBytes, e un maxInflationRatio che difende contro le bombe di
decompressione. Un documento che li sfonda è respinto, e il CdrResult lo dice
con admitted: false e un rejectionReason. Questa distinzione è portante:
«l’abbiamo pulito» e «lo abbiamo rifiutato» sono esiti diversi, e il tipo del
risultato li tiene separati così che anche la tua reportistica degli errori possa
farlo.
Le minacce sono nominate, non indovinate. ThreatDetector::detect() mappa
ogni costrutto pericoloso a un ThreatType — JavaScript, LaunchAction,
OpenAction, AdditionalActions, RemoteGoTo, SubmitForm, ImportData,
EmbeddedFiles, RichMedia, NamedJavaScript, e altri — ciascuno legato a una
specifica funzionalità PDF. Un oggetto che non può essere analizzato affatto
diventa il proprio tipo di minaccia, UnparseableObject, perché un oggetto che
il sanitizzatore non può leggere è un oggetto per cui non può garantire. Ogni
ritrovamento è un DetectedThreat che porta il numero dell’oggetto incriminato,
così che la rimozione sia precisa.
La rimozione protegge il documento, non solo il payload. Alcune chiavi
pericolose vivono sul document catalog (il Root) — una /OpenAction che scatta
all’apertura, per esempio. Rimuovere l’intero oggetto Root per eliminare una
chiave distruggerebbe il catalog e romperebbe silenziosamente il file. Il motore
gestisce invece queste come strip di chiave sul posto sul catalog, e come una
guardia fail-closed si rifiuta di emettere un documento ricostruito che ha perso
del tutto il suo /Root. Un sanitizzatore che produce un file strutturalmente
rotto mentre riporta successo è esattamente la modalità di fallimento che questa
guardia esiste per prevenire.
Esempio pratico
Sezione intitolata “Esempio pratico”La forma qui sotto è il vero punto d’ingresso. Si consegnano al motore byte
grezzi e una policy; si riceve indietro un CdrResult che è onesto su ciò che è
successo.
<?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.Non c’è alcun percorso in cui questo codice restituisce in silenzio un file
ancora pericoloso. O il documento è ammesso e disarmato, oppure è respinto con un
motivo dichiarato. La lista removedThreats significa che il disarm è
verificabile, non magico.
Equivoco comune
Sezione intitolata “Equivoco comune”«Il CDR è solo redazione con passaggi in più.»
Non lo è, e confondere i due è pericoloso. La redazione rimuove l’informazione — nomi, numeri di conto, il contenuto che un essere umano non deve vedere. Il CDR rimuove la capacità — il JavaScript, la Launch action, il payload incorporato che una macchina non deve eseguire. Hanno criteri di successo opposti. Una redazione è corretta quando il contenuto sensibile è sparito e il resto è preservato alla lettera. Un disarm è corretto quando la minaccia è sparita, ed è perfettamente disposto ad alterare struttura benigna per arrivarci. Si usi lo strumento che corrisponde alla tua intenzione; non si ricorra a uno aspettandosi le garanzie dell’altro.
Un secondo equivoco è che una ricostruzione pulita provi che l’originale fosse pulito. Non prova nulla sull’originale. Prova solo che l’output non contiene alcuna minaccia rilevata. L’input può essere stato un’arma; il compito del CDR è assicurarsi che ciò che si inoltra a valle non lo sia.
Limiti e confini
Sezione intitolata “Limiti e confini”Questa è la parte che le brochure saltano, quindi la diremo chiaramente.
- L’output è una proiezione di sicurezza, non una copia probatoria. Il
sorgente del
CdrEngineporta questo come una linea rossa architetturale. Il PDF sanitizzato non deve essere usato per la conservazione di prove legali, per il confronto di hash con l’originale, o come copia di archiviazione. La trasformazione è distruttiva e non reversibile per progetto. - Il rilevamento ha un ambito. Il CDR rimuove le minacce che sa nominare. È uno strato forte e verificabile in uno stack di difesa in profondità — non una garanzia che un file sia libero da ogni possibile tecnica futura. Lo si tenga dietro la stessa validazione degli upload, i controlli di content-type, e la gestione a privilegio minimo che descrive la guida OWASP su file-upload.
- Alcune policy sono intenzionalmente con perdita. I casi
Strip*opt-in rimuovono file incorporati, firme, campi di modulo, layer, e media 3D. Sono potenti ed elimineranno contenuto legittimo — un payload di fattura ZUGFeRD/Factur-X, per esempio. Sono disattivati per impostazione predefinita esattamente per questo motivo. Li si attivi consapevolmente. - Le firme non sopravvivono a una ricostruzione. Ricostruire il file ne cambia i byte, quindi qualsiasi firma digitale originale non corrisponde più al suo byte range. Un documento disarmato è non firmato rispetto alla sorgente. Se serve un artefatto firmato, si firmi l’output pulito come un nuovo atto.
| Edition | Availability |
|---|---|
| Core | Non disponibile. Il core di NextPDF non esegue CDR. Analizza, renderizza e scrive PDF; non rileva minacce né ricostruisce input non fidati. |
| Pro | Non disponibile nell’edizione Pro. |
| Enterprise | Disponibile tramite |
Documenti correlati
Sezione intitolata “Documenti correlati”- Come funziona davvero la cifratura dei PDF — l’altra metà della gestione dei PDF sensibili: proteggere il contenuto contro rimuovere la capacità.
- Errori come funzionalità — la filosofia
fail-closed che i rifiuti del controllo di ammissione del CDR e la guardia
/Rootincarnano. - Un’API che si rifiuta di indovinare — perché un tipo di risultato che distingue pulito da rifiutato batte un silenzioso best effort.
Glossario
Sezione intitolata “Glossario”- CDR (Content Disarm & Reconstruction) — una strategia di sanitizzazione che analizza un file non fidato, rimuove i componenti attivi o pericolosi, e ricostruisce un file pulito dal resto sicuro, anziché scandire alla ricerca di firme note-cattive.
- Proiezione di sicurezza — un output sanitizzato che preserva abbastanza della sorgente da essere utile pur garantendo la rimozione delle minacce. È deliberatamente non byte-fedele e non adatto a prove, hashing o archiviazione.
- Controllo di ammissione — il gate pre-sanitizzazione che respinge i documenti che superano i limiti di risorse (numero di oggetti, numero di pagine, dimensione dello stream decodificato, rapporto di inflazione) prima che inizi qualsiasi lavoro di disarm.
- Action — un costrutto PDF che fa accadere qualcosa su un trigger come l’apertura del documento o il cambiamento di un campo; il catalogo dei tipi di action (Spec: ISO 32000-2, §12.6.4ISO 32000-2 §12.6.4) include JavaScript e Launch action, la superficie di minaccia canonica del CDR.
- Riferimento pendente — un puntatore a un oggetto che non esiste più dopo il filtraggio. La fase di scrub dei riferimenti li rimuove così che il file ricostruito resti strutturalmente coerente.