Un flag di permesso non è un meccanismo di sicurezza. Il bit che dice "vietata la copia" vive dentro lo stesso dizionario /Encrypt della crittografia, il che gli dà un'aria di applicabilità che non possiede, e nel momento in cui trattate le due cose come una sola la vostra verifica inizia a produrre risposte sbagliate. L'unica domanda che vale la pena porre a un PDF non è "è cifrato". È più specifica e più difficile: quale algoritmo, quale revisione del security handler, quale delle due password è stata impostata, quali bit di permesso vengono dichiarati e quali parti del file la cifratura tocca davvero. Un file può essere formalmente cifrato e praticamente aperto. Può rifiutarsi di essere letto e tuttavia lasciare i propri metadati in chiaro. Può bloccare la stampa con un flag che qualsiasi visualizzatore è libero di ignorare. Verificare un PDF significa risolvere tutti questi punti separatamente, e PDF Library for Delphi, il motore PDF di losLab per Delphi e C++Builder, espone ciascuno di essi sia tramite una API flat a handle interi sia tramite un livello a classi tipizzato
Che cosa registra davvero il dizionario /Encrypt
ISO 32000-1 §7.6 definisce la sicurezza del documento attraverso una manciata di voci di dizionario, e PDF Library for Delphi le rispecchia una a una nel record TPDFEncryption. La versione del filtro V e la revisione R selezionano la famiglia di algoritmi. Length porta la dimensione della chiave. I bit di permesso stanno in P, le stringhe di validazione della password proprietario e utente in O e U (con OE e UE aggiunti per AES-256), un flag EncryptMetadata viaggia accanto, e altri tre campi nominano i crypt filter applicati rispettivamente a stringhe, stream e file incorporati
Il valore di questo record sta nel fatto che non interpreta nulla al posto vostro. Restituisce il dizionario grezzo e lascia a voi trarre le conclusioni, che è esattamente ciò di cui una verifica ha bisogno. Il caso del testo in chiaro dentro un file cifrato affiora in StringFilterIdentity e StreamFilterIdentity: quando uno dei due è vero, i dati corrispondenti attraversano il filtro Identity intatti, indipendentemente da ciò che riporta lo stato di cifratura del documento. Uno scanner che si ferma alla presenza di un dizionario /Encrypt definirà protetto un file simile mentre le sue stringhe e i suoi stream se ne stanno in chiaro. La stessa sfumatura governa i metadati. Quando EncryptMetadata è falso il pacchetto XMP resta leggibile a qualsiasi indicizzatore mentre il contenuto delle pagine non lo è, cosa che conviene sapere nell'istante in cui le vostre regole di instradamento si basano su un campo titolo o autore
Una breve sonda di sicurezza con l'API flat
Per la maggior parte delle pipeline, quattro chiamate flat rispondono alle domande di tutti i giorni. LoadFromFile restituisce 1 in caso di successo, e una volta aperto il documento gli ispettori di cifratura riportano rispetto al suo stato decifrato:
var
PDF: TPDFlib;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile('contract.pdf', UserPassword) <> 1 then
raise Exception.Create('Open failed: wrong password or damaged file');
Writeln('status : ', PDF.EncryptionStatus); // decifrato / cifrato / sconosciuto
Writeln('algorithm : ', PDF.EncryptionAlgorithm); // famiglia RC4 o AES
Writeln('strength : ', PDF.EncryptionStrength); // classe di lunghezza della chiave
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword conta più di quanto la sua firma su una riga lasci intendere. Il PDF definisce due password con poteri diseguali. La password utente è necessaria per aprire il file. La password proprietario concede pieni diritti e scavalca ogni bit di permesso. I byte su disco sono identici in entrambi i casi, ma una sessione aperta con la password proprietario può fare cose che la sessione con password utente non può, quindi una verifica che non registra quale credenziale è stata presentata registra metà della verità. Il livello a classi rende la distinzione interrogabile. TPDFDocument.HasUserPassword e HasOwnerPassword riportano ciò che il file richiede, mentre IsUserPassword e IsOwnerPassword riportano quale password ha effettivamente aperto la sessione corrente. Registrate quel fatto nel log. Non registrate mai i valori delle password
La scala Strength, dove "AES-256" significa due cose
Le funzioni flat Encrypt e EncryptFile accettano un intero Strength con cinque valori significativi: 0 per RC4 a 40 bit, 1 per RC4 a 128 bit, 2 per AES a 128 bit leggibile da Acrobat 7, 3 per AES a 256 bit come introdotto con Acrobat 9, e 4 per AES a 256 bit come richiesto da Acrobat X e successivi
La parte interessante è che 3 e 4 sono entrambi etichettati AES-256 e non sono lo stesso schema. Strength 3 corrisponde alla revisione 5 del security handler, un progetto intermedio che Acrobat 9 distribuì e che ISO non adottò mai. Strength 4 corrisponde alla revisione 6, la cui funzione di derivazione della chiave è stata irrobustita e standardizzata in ISO 32000-2. Per un documento che state creando oggi non c'è alcun motivo di scegliere 3 invece di 4. Per una verifica la differenza è decisiva: una policy che recita "AES-256 secondo ISO 32000-2" è soddisfatta soltanto da R6, e un file R5 che si autodefinisce AES-256 viola quella policy pur superando un ingenuo controllo di robustezza. Il livello a classi tiene i due separati per nome, esAES256Bit per R5 contro esAES256BitAcroX per R6, e la proprietà EncryptionAcroX risponde alla domanda sulla revisione con un solo booleano
I bit di permesso e le loro clausole in piccolo sulla lunghezza della chiave
EncodePermissions impacchetta otto flag nell'intero che Encrypt e EncryptFile si aspettano. Stampa, copia, modifica e aggiunta di note compongono il set di base; compilazione dei campi, copia per accessibilità, assemblaggio e stampa a piena qualità compongono il set esteso. La clausola in piccolo, che la demo di cifratura della libreria stessa dichiara apertamente, è che i quattro estesi hanno effetto solo a partire dalla robustezza a 128 bit. Il flag di stampa a piena qualità ricade sotto la stessa regola: azzeratelo per forzare la stampa a bassa risoluzione e un documento a 40 bit vi ignorerà, perché anche quel declassamento richiede una cifratura a 128 bit o superiore. Codificate una policy di "sola stampa a bassa risoluzione" in un file a 40 bit e ogni visualizzatore stamperà comunque a piena qualità
La domanda più profonda è chi applichi mai questi bit, e la risposta è nessuno di cui possiate fidarvi. I permessi sono istruzioni per i lettori conformi, non restrizioni crittografiche. La chiave di decifratura è identica sia che la copia sia consentita sia che sia negata, quindi un set di permessi restrittivo si limita a mantenere onesti i visualizzatori onesti. Un lettore che sceglie di ignorare i bit non incontra alcun ostacolo crittografico. Se l'obbligo è impedire l'estrazione anziché scoraggiarla, il file ha bisogno di una password utente e il flusso di lavoro ha bisogno di controlli a livello di processo attorno a esso, e un rapporto di verifica dovrebbe indicare sotto quale dei due regimi ricade davvero ogni file, anziché trattare un flag di permesso come un lucchetto
Impostare la policy e dimostrare che ha attecchito
Applicare la cifratura a file esistenti non richiede di caricarli nell'albero degli oggetti. EncryptFile elabora l'input verso l'output in una sola chiamata, e il ciclo di verifica riapre il risultato per confermare che cosa sia finito su disco. La demo di cifratura inclusa segue la stessa forma scrivi-poi-rileggi:
var
PDF: TPDFlib;
R: Integer;
begin
PDF := TPDFlib.Create;
try
R := PDF.EncryptFile('in.pdf', 'out.pdf', 'owner-secret', 'user-secret', 4,
PDF.EncodePermissions(1, 0, 0, 0, // stampa consentita; copia/modifica/note negate
0, 0, 0, 1)); // set esteso: solo stampa a piena qualità
if (R = 1) and (PDF.LoadFromFile('out.pdf', 'user-secret') = 1) then
begin
Writeln('algorithm = ', PDF.EncryptionAlgorithm);
Writeln('strength = ', PDF.EncryptionStrength);
Writeln('owner pw accepted: ', PDF.CheckPassword('owner-secret'));
end;
finally
PDF.Free;
end;
end;
I team che lavorano al livello del documento ottengono la stessa operazione con insiemi tipizzati al posto dell'impacchettamento di bit, cosa che sopravvive a una code review con molta meno fatica:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
In entrambi i casi, il passo di rilettura non è cerimonia opzionale. Intercetta gli errori di deployment che altrimenti affiorano mesi dopo sulla macchina di un cliente: una vecchia build della libreria che declassa silenziosamente la robustezza richiesta, un percorso di output mai scritto perché la directory era in sola lettura, un intero di permessi i cui argomenti sono entrati nell'ordine sbagliato. Tutti e tre superano uno smoke test locale e falliscono sul campo, e riaprire l'output trasforma ciascuno di essi in un'eccezione che vedete durante l'esecuzione che ha creato il file. GetEncryptionFingerprint restituisce un valore compatto che potete conservare con il record del job, così un confronto successivo può dire se due output condividono la stessa configurazione di cifratura senza riaprire nessuno dei due
Falsi positivi di verifica per cui vale la pena scrivere codice
Alcuni schemi spingono con regolarità gli scanner di sicurezza verso la conclusione sbagliata, e ciascuno nasce dal ridurre una domanda a più parti a una risposta sì o no. Il crypt filter Identity è l'esempio più limpido. Un dizionario /Encrypt è presente, il file si dichiara cifrato, eppure le stringhe e gli stream attraversano invariati il filtro Identity, quindi il contenuto reale è in chiaro. Leggere StringFilterIdentity e StreamFilterIdentity prima di dichiarare protetto qualsiasi cosa è il rimedio
La divisione dei metadati è più sottile. EncryptMetadata può discordare dal resto del documento in entrambe le direzioni, lasciando un file cifrato con un pacchetto XMP leggibile o, meno spesso, il contrario. "Il file è cifrato" non dice nulla sul fatto che lo siano i suoi metadati, cosa che conta nell'istante in cui un indicizzatore o una regola di instradamento va a prendere il titolo. I file incorporati aggiungono un terzo asse: il PDF consente un crypt filter dedicato solo agli allegati, quindi gli allegati possono essere l'unica parte cifrata di un documento altrimenti aperto, o l'unica parte in chiaro di uno cifrato. Catturate le tre assegnazioni di filtro come campi separati per stringhe, stream e file incorporati, e nessuna di queste trappole potrà cogliervi. Memorizzate un solo booleano e la chiamata sbagliata sarà solo questione di tempo
Rimuovere la cifratura, e sceglierla per i nuovi file
Una verifica si conclude spesso con la decisione di togliere la protezione, e la meccanica non è l'ostacolo. DecryptFile(InputFileName, OutputFileName, Password) scrive una copia decifrata senza un caricamento completo, e il metodo Decrypt sul documento caricato fa lo stesso in memoria quando un file è già aperto. Entrambi richiedono una password valida; nessuno dei due aggira la crittografia. Il vero cancello è la policy anziché il codice, quindi fate in modo che le vostre regole di accettazione dichiarino con chiarezza quando la rimozione è consentita e registrate la classe di password che l'ha autorizzata, perché il passo tecnico in sé non lascia alcuna traccia
La scelta per i nuovi output è più ristretta di quanto i cinque valori di Strength lascino intendere. Usate Strength 4, AES-256 revisione 6, a meno che non dobbiate aprire i file in visualizzatori più vecchi di Acrobat X. Strength 2, AES-128, è il pavimento pragmatico per un parco visualizzatori invecchiato che non può essere aggiornato. Le opzioni RC4 a 0 e 1 esistono perché possiate leggere e verificare archivi storici, non perché ci produciate qualcosa di nuovo; ricorrervi in un progetto del 2026 è il segno che un requisito a monte è ormai obsoleto
Lo stato di cifratura alimenta direttamente le decisioni sulla firma, poiché un workbench che valida e firma documenti ha bisogno della stessa disciplina di rilettura su cui questa verifica si appoggia. Quel terreno è trattato nell'articolo sul workbench di conformità e firma. Quando un lotto applica EncryptFile a migliaia di documenti di grandi dimensioni, la guida ad accesso diretto ai PDF di grandi dimensioni mostra come tenere piatta la memoria durante l'esecuzione. Il riferimento completo dell'API di cifratura si trova sulla pagina prodotto di PDF Library for Delphi