Articolo tecnico

Crittografare l'output XLSX con AES in Delphi: cosa scrive SaveAsEncrypted di HotXLS

Excel espone due elementi entrambi definiti "password", ma solo uno di essi rappresenta la reale crittografia. La password di apertura chiave cifra il file in modo reale: senza di essa, il file non può essere letto. Le password di protezione del foglio e della cartella di lavoro non eseguono nulla del genere. Esse configurano un flag che un editor conforme accetta di rispettare, ma una cartella di lavoro contenente solo tale flag rimane un semplice archivio zip leggibile con i dati in testo semplice. Scegliere l'opzione sbagliata significa inviare un foglio paga che appare protetto in Excel ma che può essere letto in qualsiasi editor di testo

La prova richiede dieci secondi. Rinomina un file .xlsx protetto in .zip, aprilo con un qualsiasi strumento di archiviazione ed esamina xl/worksheets/sheet1.xml. Se i valori delle celle sono visibili in semplice UTF-8, il file non è crittografato, indipendentemente dal numero di richieste di password che Excel presenta quando si tenta di modificare una cella. Questa lacuna persiste per anni all'interno di team che confondono la protezione del foglio con la riservatezza dei dati, e di solito emerge quando un controllo di sicurezza esegue proprio questa rinomina

HotXLS è una libreria di fogli di calcolo nativa per Delphi e C++Builder che mantiene queste due funzionalità ben distinte. La protezione del foglio di lavoro e della cartella di lavoro rappresenta una restrizione di modifica supportata da un hash legacy deliberatamente debole. SaveAsEncrypted produce un pacchetto crittografato con AES che non si aprirà senza la password. Le sezioni seguenti descrivono cosa scrive questa chiamata, l'asimmetria che occorre considerare nella progettazione (HotXLS scrive file crittografati ma non può rileggerli) e le differenze con il vecchio percorso XLS

Perché la protezione del foglio non corrisponde alla crittografia

I metodi Protect sui fogli e ProtectWorkbook sulla cartella di lavoro memorizzano un hash a 4 cifre esadecimali della password. Questo è l'algoritmo legacy che sia OOXML sia BIFF hanno ereditato dall'Excel degli anni '90, e la documentazione del formato non dichiara in alcun punto di fare di più che prevenire modifiche accidentali. Il pacchetto rimane un normale archivio zip leggibile: dati delle celle, formule e stringhe condivise sono tutti in XML in testo semplice. Il comportamento predefinito peggiora le cose, anziché migliorarle. Ogni cella inizia con Locked=True, per cui chiamare Protect senza prima sbloccare un intervallo di input blocca l'intero foglio per le modifiche lasciando ogni valore visibile in chiaro

Nulla di tutto ciò rende la protezione inutile. Guidare gli utenti verso intervalli modificabili e stabilizzare un layout per la stampa sono attività utili, descritte nel nostro articolo sulla protezione dei fogli di lavoro e impostazione della pagina. Ma si tratta di attività di usabilità. Quando il requisito fondamentale è la riservatezza, l'unica API da utilizzare è SaveAsEncrypted

Cosa scrive effettivamente SaveAsEncrypted

L'implementazione segue lo standard ECMA-376 Standard Encryption, specificato in [MS-OFFCRYPTO] sezione 2.3.4. La password viene elaborata tramite 50.000 iterazioni di SHA-1 per derivare una chiave AES-128. Un blocco di verifica, crittografato con AES-128 in modalità ECB, consente al consumatore di confermare la password prima di decrittografare qualsiasi dato, e l'intero pacchetto della cartella di lavoro viene quindi crittografato con AES-128 in modalità CBC. Ciò che viene salvato su disco non è affatto uno zip, ma un file composto OLE contenente i flussi EncryptionInfo, EncryptedPackage e DataSpaces, senza la directory xl/ visibile da uno strumento di archiviazione, motivo per cui il test di rinomina non mostrerà nulla di leggibile. Excel 2007 e versioni successive lo aprono con la sola password, e anche l'attuale versione di LibreOffice supporta la crittografia Standard Encryption

var
  Book: TXLSXWorkbook;
  Sheet: TXLSXWorksheet;
  rc: Integer;
begin
  Book := TXLSXWorkbook.Create;
  try
    Sheet := Book.Sheets.Add('Payroll');
    Sheet.Cells[1, 1].Value := 'Employee';
    Sheet.Cells[1, 2].Value := 'Net pay';
    Sheet.Cells[2, 1].Value := 'A. Garcia';
    Sheet.Cells[2, 2].Value := 4815.16;

    rc := Book.SaveAsEncrypted('payroll-2026-06.xlsx', PasswordFromVault);
    if rc <> 1 then
      raise Exception.CreateFmt('Encrypted save failed (rc=%d)', [rc]);
  finally
    Book.Free;
  end;
end;

Tratta la variabile della password con la stessa attenzione riservata a una stringa di connessione. Recuperala da un vault o da un servizio di generazione segreti all'ultimo momento, non registrarla nei log e non scriverla mai nella cartella di lavoro stessa. Il controllo del codice di ritorno non è opzionale. Un salvataggio crittografato che fallisce parzialmente deve interrompere la consegna del file, poiché l'unico fallback che il codice chiamante può offrire è una copia non crittografata, evento che questa funzione mira proprio a evitare

Esiste anche un test di accettazione automatizzabile a costo quasi nullo: chiamare CanReadEncrypted sul file appena scritto. Restituisce true solo quando l'output è effettivamente un contenitore crittografato, per cui eseguire questa verifica dopo ogni salvataggio crittografato intercetta la regressione più critica (un percorso di codice che è tornato silenziosamente a un normale SaveAs) nel momento stesso in cui si verifica, anziché settimane dopo nella posta del cliente. L'ultima parola spetta comunque a un'apertura manuale in Excel con la password reale durante i test di rilascio

Sola scrittura per progettazione: gestione di EXlsxEncryptionNotImplemented

Ecco l'asimmetria che dovrebbe configurare l'architettura della tua pipeline: HotXLS esegue la crittografia al salvataggio ma non la decrittografia all'apertura. OpenEncrypted solleva l'eccezione EXlsxEncryptionNotImplemented quando punta a un pacchetto crittografato reale; su una cartella di lavoro semplice passa direttamente al normale Open. La funzione di controllo CanReadEncrypted rileva rapidamente il contenitore di crittografia OLE, consentendo al codice di smistare tali file senza attivare l'eccezione:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.CanReadEncrypted(FileName) then
    begin
      // Encrypted container: HotXLS cannot decrypt it.
      Writeln(FileName + ': needs manual decryption in Excel first');
      Exit;
    end;
    try
      Book.OpenEncrypted(FileName, '');   // plain files fall through to Open
      Writeln(FileName + ': opened, ' + IntToStr(Book.Sheets.Count) + ' sheet(s)');
    except
      on EXlsxEncryptionNotImplemented do
        Writeln(FileName + ': encrypted - routed to manual queue');
    end;
  finally
    Book.Free;
  end;
end;

Questa asimmetria offre un'indicazione architetturale precisa: crittografare solo alla fine, al momento della consegna. Mantieni il file original in chiaro entro i confini della tua rete di fiducia (in un database, in un archivio documenti o in una condivisione con controllo degli accessi) e produci la copia crittografata come passaggio finale prima che il file esca dal sistema. Una pipeline che archivia solo l'output crittografato rischia di perdere l'accesso ai propri dati, poiché nessuna fase successiva del sistema potrà riaprire tali file. Quando un processo HotXLS a valle necessita nuovamente della cartella di lavoro, passa il file master in chiaro, mai l'artefatto di consegna

AES-128 Standard Encryption e il requisito di conformità AES-256

La crittografia dei file di Office è divisa in due generazioni. Lo schema Standard Encryption, generato da HotXLS, utilizza AES-128 con derivazione di chiave SHA-1. Lo schema Agile Encryption è arrivato successivamente e adotta AES-256 con SHA-512 e un diverso contenitore di chiavi descritto in XML. Entrambi si aprono in modo trasparente in Excel, ed AES-128 rimane una soluzione valida per proteggere un file durante il trasferimento a un cliente

La differenza cessa di essere puramente accademica il giorno in cui un questionario di sicurezza richiede la "crittografia AES-256 dei file a riposo". Lo schema Standard Encryption non soddisfa questo requisito, indipendentemente dalla complessità della password, e nessun parametro di SaveAsEncrypted modifica l'algoritmo emesso. Si consiglia pertanto di descrivere con precisione il profilo nella documentazione di sicurezza: AES-128, ECMA-376 Standard Encryption, derivazione della chiave SHA-1 a 50.000 iterazioni. Una dichiarazione verificabile ha più valore di una ottimistica che crolla in fase di audit

Il percorso XLS legacy: RC4 in uscita, RC4 e XOR in entrata

L'interfaccia BIFF presenta una struttura opposta. La sua crittografia è più vecchia e meno sicura, ma il round-trip is completo: ciò che scrive può anche essere riletto. Impostare EncryptionPassword prima di SaveAs produce un file .xls crittografato con RC4 tramite il meccanismo BIFF FilePass, e la funzione Open con parametro password legge tutti e tre gli schemi legacy: RC4, RC4 CryptoAPI e l'antico offuscamento XOR:

var
  Writer, Reader: IXLSWorkbook;   // interface refs: no manual Free
begin
  Writer := TXLSWorkbook.Create;
  Writer.Sheets.Add.Cells.Item[1, 1].Value := 'Confidential';
  Writer.EncryptionPassword := 'S3cret!';
  Writer.SaveAs('confidential.xls');

  Reader := TXLSWorkbook.Create;
  if Reader.Open('confidential.xls', 'S3cret!') > 0 then
    Writeln(Reader.Sheets[1].Cells.Item[1, 1].Value);  // Entries are 1-based
end;

RC4 rappresenta una crittografia obsoleta e non dovrebbe mai essere utilizzata per proteggere dati sensibili odierni; il suo unico valore risiede nell'interoperabilità con sistemi che scambiano ancora file .xls. Il lato di lettura, tuttavia, si rivela utile nelle attività di migrazione. Un file legacy protetto da password si apre con Open(FileName, Password), viene importato nel modello OOXML e protetto nuovamente tramite il percorso AES, un aggiornamento unidirezionale eseguito senza Excel nel processo. Per consegne crittografate ad alto volume, le note sulle prestazioni in scrittura nel nostro articolo sulle scritture in streaming per processi batch su server si applicano alla fase di compilazione dei contenuti prima della crittografia

Crittografia e protezione non sono in concorrenza

Un ultimo punto da chiarire, poiché si presenta quando qualcuno interpreta l'avvertimento in cima a questa pagina come "la protezione non ha valore". Non è così. Crittografia e protezione rispondono a esigenze diverse e si integrano a vicenda. La crittografia determina chi può aprire il file; la protezione stabilisce cosa un lettore autorizzato all'accesso possa modificare. Per l'invio di un foglio paga è ragionevole fare entrambe le cose: crittografare il pacchetto in modo che solo chi possiede la password possa visualizzarlo, e bloccare le celle con formule in modo che il destinatario possa filtrare e ordinare i dati senza modificare i calcoli. L'errore non risiede nell'aggiungere la protezione, ma nel confonderla con la crittografia quando il requisito primario è la riservatezza

La gestione delle chiavi non prevede protezioni di recupero, e questo per progettazione. La derivazione della chiave a 50.000 iterazioni è studiata per rendere complessi i tentativi di decifratura, e nessuna chiave segreta viene salvata nel file. Una password smarrita equivale a dati persi. Genera, consegna e conserva queste password con la stessa cura applicata alle credenziali dei database, e la crittografia farà il suo dovere

La crittografia reale dei file si riduce a una sola chiamata in HotXLS. L'attenzione deve concentrarsi su tutto ciò che circonda tale chiamata: la custodia della password, il limite di sola scrittura che impedisce a HotXLS di riaprire il proprio output e una documentazione dell'algoritmo che superi eventuali audit. La funzione SaveAsEncrypted e il round-trip legacy sono integrati nel HotXLS Component, che viene eseguito in modo nativo nei processi Delphi e C++Builder senza l'automazione di Excel