Una pipeline di intake documentale una volta mise in quarantena un intero batch di atti legali come "cifrato", anche se ogni file si apriva in Adobe Acrobat senza prompt password. La sonda aveva ridotto lo stato di sicurezza a un singolo boolean. I file portavano davvero un dictionary /Encrypt, ma i loro crypt filter erano impostati su Identity, cioè stringhe e stream erano memorizzati in plaintext. Formalmente cifrati, praticamente aperti, e bloccati per due giorni sulla forza di un flag letto male. Quell'incidente è una buona definizione di cosa debba essere un vero audit di cifratura: non "è cifrato", ma quale algoritmo, quale revisione, quali password, quali permessi, e quali parti del file la cifratura tocca davvero. PDFlibPas, motore PDF losLab per Delphi e C++Builder, espone tutte queste risposte sia tramite una flat API sia tramite un class layer tipizzato.
Cosa registra davvero il dictionary /Encrypt
ISO 32000-1 §7.6 definisce la sicurezza documento tramite alcune entry di dictionary, e PDFlibPas le rispecchia una per una nel record TPDFEncryption: la versione filter V e la revisione R che selezionano la famiglia algoritmo, Length per la dimensione chiave, i bit di permesso in P, le stringhe di validazione owner e user password O e U, più OE/UE per AES-256, un flag EncryptMetadata, e i nomi dei crypt filter applicati a stringhe, stream e file incorporati.
Il record è amico dell'auditor proprio perché non interpreta. L'incidente di intake sopra diventa visibile in campi come StringFilterIdentity e StreamFilterIdentity: quando sono true, i dati corrispondenti non vengono trasformati affatto, qualunque cosa dichiari lo stato cifrato del documento. Allo stesso modo EncryptMetadata = False ti dice che i metadati XMP sono leggibili da qualunque indicizzatore anche se il contenuto pagina non lo è, rilevante quando le regole di routing dipendono da titolo o autore.
Una sonda sicurezza in dieci righe con la flat API
Per la maggior parte delle pipeline, quattro chiamate flat rispondono alle domande quotidiane. LoadFromFile restituisce 1 al successo; dopo, gli inspector a livello documento lavorano contro lo 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); // decrypted / encrypted / unknown
Writeln('algorithm : ', PDF.EncryptionAlgorithm); // RC4 vs AES family
Writeln('strength : ', PDF.EncryptionStrength); // key length class
Writeln('owner pw? : ', PDF.CheckPassword(CandidatePassword));
finally
PDF.Free;
end;
end;
CheckPassword conta più di quanto sembri. PDF distingue una user password, richiesta per aprire, da una owner password, che concede pieni diritti e supera i permessi, e un file aperto con user password si comporta in modo molto diverso da uno aperto con owner password: stessi byte, diritti diversi. Il class layer rende esplicita la distinzione: TPDFDocument.HasUserPassword e HasOwnerPassword riportano cosa è impostato, mentre IsUserPassword e IsOwnerPassword riportano quale ha effettivamente aperto la sessione corrente. Un audit log dovrebbe registrare quella distinzione, mai i valori delle password.
La scala Strength: da RC4 40-bit ad AES-256 revisione 6
Le funzioni flat Encrypt e EncryptFile prendono un intero Strength con cinque valori significativi: 0 per RC4 40-bit, 1 per RC4 128-bit, 2 per AES 128-bit, leggibile da Acrobat 7, 3 per AES 256-bit introdotto con Acrobat 9, e 4 per AES 256-bit richiesto da Acrobat X e successivi.
I valori 3 e 4 meritano uno sguardo più attento, perché "AES-256" sono due schemi diversi. Strength 3 mappa al security handler revision 5, un disegno intermedio distribuito in Acrobat 9 e mai adottato da ISO. Strength 4 mappa alla revision 6, lo schema con funzione di derivazione chiave rafforzata standardizzata da ISO 32000-2. Per nuovi documenti non c'è motivo di scegliere 3; per audit, la differenza conta perché una policy di conformità che dice "AES-256 per ISO 32000-2" è soddisfatta solo da R6. In lettura, il class layer separa i due come esAES256Bit contro esAES256BitAcroX, e la proprietà EncryptionAcroX risponde direttamente alla domanda R5-o-R6.
Bit di permesso e nota fine sulla lunghezza chiave
EncodePermissions impacchetta otto flag nell'intero atteso da Encrypt e EncryptFile: print, copy, change e add-notes formano il set base, mentre fill-fields, copy-for-accessibility, assemble e full-quality print formano il set esteso. La nota fine, esplicitata nella demo encryption della libreria, è che i quattro estesi sono onorati solo a strength 128-bit e superiori, e lo stesso vale per degradare la qualità di stampa impostando il flag full-quality-print a 0. Codifica una policy "solo stampa a bassa risoluzione" in un documento 40-bit e i viewer stamperanno semplicemente a qualità piena.
Il caveat più profondo è chi applica questi bit. I permessi in PDF sono istruzioni ai reader conformi, non restrizioni crittografiche: la chiave di decifratura è la stessa che la copia sia permessa o no. Un set di permessi bloccato mantiene onesti i viewer onesti. Se il tuo obbligo è impedire l'estrazione invece che scoraggiarla, serve una user password e controlli a livello processo, e il report di audit dovrebbe essere esplicito su quale dei due regimi governa il file.
Impostare policy e provare che sia rimasta
Applicare cifratura a file esistenti non richiede caricarli nell'object tree. EncryptFile elabora input verso output in una chiamata, e il ciclo di audit riapre il risultato per verificare l'esito, un pattern quasi identico a quello della demo distribuita:
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, // print allowed; copy/change/notes denied
0, 0, 0, 1)); // extended set: full-quality print only
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 a livello documento ottengono la stessa operazione con set tipizzati invece di bit packing, più leggibili in code review:
if not Doc.Encrypt('owner-secret', 'user-secret', esAES256BitAcroX,
[ppCanPrint], [ppCanPrintFull]) then
raise Exception.Create('Encryption failed');
In entrambi i casi, il passaggio di rilettura non è cerimonia facoltativa. Intercetta gli errori classici di deployment, una versione vecchia della libreria che degrada silenziosamente la strength richiesta, un path di output mai scritto, un intero permissions assemblato nell'ordine argomenti sbagliato, nel momento in cui avvengono invece che alla scrivania del cliente. GetEncryptionFingerprint ti dà un valore compatto da salvare con il record del job per confronti futuri.
Falsi positivi di audit per cui vale la pena scrivere codice
Tre pattern producono ripetutamente conclusioni sbagliate negli scanner di sicurezza. Primo, il caso crypt filter Identity dell'apertura: dictionary /Encrypt presente, contenuto reale intatto; controlla i flag Identity per filtro prima di dichiarare protetti i dati. Secondo, lo split dei metadati: EncryptMetadata può discordare con il resto del file in entrambe le direzioni, quindi "il file è cifrato" non dice nulla su se il pacchetto XMP lo sia. Terzo, file incorporati: PDF permette un crypt filter dedicato per gli allegati, quindi gli allegati possono essere l'unica parte cifrata di un documento altrimenti aperto, o l'unica parte plaintext di uno cifrato. Un record di audit che cattura separatamente le tre assegnazioni filter, stringhe, stream e file incorporati, è immune a tutte e tre le trappole; uno che salva un boolean sbaglia puntualmente.
Domande che gli auditor fanno davvero
Posso rimuovere la cifratura da un file quando ho la password? Sì: DecryptFile(InputFileName, OutputFileName, Password) lo fa senza un load completo, e Decrypt sul documento caricato fa lo stesso in memoria. Se tu possa farlo è una domanda di policy a cui le regole di intake dovrebbero rispondere esplicitamente.
Quale strength dovrebbero usare i nuovi documenti? Strength 4, AES-256 revision 6, a meno che tu debba supportare viewer più vecchi di Acrobat X. Strength 2, AES-128, resta il floor pragmatico per flotte di viewer molto vecchie; le opzioni RC4 esistono per audit di compatibilità, non per nuovo output.
I flag di permesso fermano un utente determinato dal copiare testo? No. Sono onorati dai viewer conformi, e ISO 32000 li inquadra come permessi di accesso da far applicare ai reader, non come crittografia. Abbinali a una user password quando la riservatezza è il requisito reale.
Ulteriori letture
Lo stato di cifratura alimenta direttamente decisioni di firma: un workbench che valida e firma documenti richiede la stessa disciplina di rilettura, come trattato nell'articolo sul workbench di conformità e firma. Per pipeline batch che applicano EncryptFile a migliaia di documenti grandi, la guida direct-access ai PDF grandi mostra come mantenere piatta la memoria mentre lo fai.
Il riferimento API completo sulla cifratura vive nella pagina prodotto PDFlibPas.