Technical Article

Šifrovanie výstupu XLSX pomocou AES v Delphi: čo zapisuje HotXLS SaveAsEncrypted

Excel ponúka dve veci, ktoré sa zhodne nazývajú „heslo“, no iba jedna z nich predstavuje skutočné šifrovanie. Heslo na otvorenie riadi skutočnú šifru: bez neho nie je možné súbor vôbec prečítať. Heslá na ochranu pracovného hárka a zošita nič také nerobia. Nastavujú iba príznak, ktorý sa spolupracujúci editor zaväzuje rešpektovať, a zošit obsahujúci iba tento príznak je obyčajný čitateľný zip archív, v ktorom sú dáta uložené ako otvorený text (cleartext). Ak vyberiete nesprávnu možnosť, môžete odoslať výplatnú pásku, ktorá síce v Exceli vyzerá uzamknutá, ale dá sa prečítať v akomkoľvek textovom editore.

Dôkaz trvá desať sekúnd. Premenujte chránený súbor .xlsx na .zip, otvorte ho v ľubovoľnom archivačnom nástroji a pozrite sa do xl/worksheets/sheet1.xml. Ak sú hodnoty buniek v obyčajnom UTF-8, súbor nie je zašifrovaný bez ohľadu na to, koľko výziev na zadanie hesla Excel zobrazí pri pokuse o úpravu bunky. Táto bezpečnostná medzera prežíva roky v tímoch, ktoré predpokladajú, že ochrana hárka znamená dôvernosť, a zvyčajne sa prejaví v deň, keď bezpečnostný audit vykoná presne toto premenovanie.

HotXLS je natívna knižnica pre tabuľkové procesory v Delphi a C++Builder, ktorá drží tieto dve funkcie na opačných stranách barikády. Ochrana pracovného hárka a zošita predstavuje obmedzenie úprav podložené zámerne slabým historickým hašom. Metóda SaveAsEncrypted vytvára balík šifrovaný pomocou AES, ktorý neotvorí nič iné ako správne heslo. Nasledujúce sekcie popisujú, čo toto volanie zapisuje, asymetriu, s ktorou musíte počítať pri návrhu (HotXLS zapisuje šifrované súbory, ale nedokáže ich prečítať späť), a ako sa líši staršia cesta pre formát XLS.

Prečo ochrana hárka nie je šifrovanie

Metódy Protect na hárkoch a ProtectWorkbook na zošite ukladajú 4-miestny hexadecimálny haš hesla. Ide o starší algoritmus, ktorý formáty OOXML aj BIFF zdedili z Excelu z 90. rokov, pričom dokumentácia k formátu nikdy netvrdila, že robí niečo viac, než len zabraňuje náhodným úpravám. Balík zostáva obyčajným čitateľným zip archívom: údaje buniek, vzorce a zdieľané reťazce sú uložené v XML ako otvorený text. Predvolené nastavenie situáciu skôr zhoršuje než zlepšuje. Každá bunka začína s hodnotou Locked=True, pričom volanie Protect bez predchádzajúceho odomknutia vstupného rozsahu zmrazí celý hárok pred úpravami, no ponechá všetky hodnoty jasne viditeľné.

Nič z toho však nerobí ochranu zbytočnou. Navádzanie používateľov do editovateľných rozsahov a stabilizácia rozloženia na tlač sú reálne úlohy, ktorým sa venuje náš článok o ochrane pracovného hárka a nastavení strany. Ide však o úlohy týkajúce sa použiteľnosti. Akonáhle je požiadavkou dôvernosť, jediným rozhraním API, ktoré ju rieši, je SaveAsEncrypted.

Čo skutočne zapisuje SaveAsEncrypted

Implementácia nasleduje štandardné šifrovanie ECMA-376 špecifikované v dokumente [MS-OFFCRYPTO] v sekcii 2.3.4. Heslo prechádza 50 000 iteráciami SHA-1 na odvodenie 128-bitového kľúča AES. Overovací blok zašifrovaný pomocou AES-128 v režime ECB umožňuje overiť heslo pred dešifrovaním a celý balík zošita je potom zašifrovaný pomocou AES-128 v režime CBC. To, čo sa uloží na disk, vôbec nie je zip. Je to zložený súbor OLE obsahujúci toky EncryptionInfo, EncryptedPackage a DataSpaces, bez adresára xl/, ktorý by archivačný nástroj mohol zobraziť. Preto test s premenovaním neukáže nič čitateľné. Excel 2007 a novší ho otvorí iba s heslom a aktuálny LibreOffice taktiež dokáže prečítať štandardné šifrovanie.

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;

Zaobchádzajte s premennou hesla s rovnakou opatrnosťou ako s pripájacím reťazcom. Získajte ho z trezoru alebo služby na generovanie tajných kľúčov v poslednej chvíli, nikdy ho nezaznamenávajte do logov a nezapisujte priamo do samotného zošita. Kontrola návratového kódu nie je len nepovinná formalita. Uloženie šifrovaného súboru, ktoré zlyhá v priebehu procesu, musí prerušiť doručenie, pretože jediným náhradným riešením, ktoré volajúci kód môže ponúknuť, je nezašifrovaná kópia. A práve takejto kópii má táto funkcia zabrániť.

Existuje aj strojovo overiteľný test prijatia, ktorý nestojí takmer nič: zavolajte metódu CanReadEncrypted na súbor, ktorý ste práve zapísali. Vracia hodnotu true iba vtedy, keď je výstup skutočne šifrovaným kontajnerom. Jej overenie po každom šifrovanom uložení zachytí tú najdôležitejšiu regresiu, konkrétne kódovú cestu, ktorá ticho prešla na obyčajné SaveAs, a to hneď v momente vzniku, a nie až o niekoľko týždňov neskôr v doručenej pošte zákazníka. Posledné slovo má však stále manuálne otvorenie v Exceli so skutočným heslom počas testovania vydania.

Návrh typu write-only (iba na zápis): ošetrenie výnimky EXlsxEncryptionNotImplemented

Tu je asymetria, ktorá by mala formovať architektúru vášho spracovania dát (pipeline): HotXLS šifruje pri ukladaní, ale nedešifruje pri otváraní. Metóda OpenEncrypted vyvolá výnimku EXlsxEncryptionNotImplemented, ak smeruje na skutočne zašifrovaný balík. Pri obyčajnom zošite jednoducho prejde na bežné Open. Pomocná detekcia CanReadEncrypted lacno identifikuje šifrovaný kontajner OLE, takže spracovateľský kód môže takéto súbory presmerovať bez vyvolania výnimky:

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;

Táto asymetria má jasný architektonický význam: šifrujte až na samom konci doručovacieho reťazca. Udržujte otvorený text (plaintext) v rámci vašej dôveryhodnej zóny (v databáze, úložisku dokumentov alebo na zdieľanom disku s riadeným prístupom) a šifrovanú kópiu vytvorte ako posledný krok pred opustením systému. Spracovateľský reťazec, ktorý archivuje iba šifrovaný výstup, sa uzamkne pred vlastnými údajmi, pretože žiadna neskoršia fáza toho istého systému nedokáže tieto súbory znova otvoriť. Keď následný proces HotXLS potrebuje zošit znova, poskytnite mu otvorený text (plaintext), nikdy nie doručovaný šifrovaný artefakt.

Štandardné šifrovanie AES-128 a požiadavka na súlad s AES-256

Šifrovanie súborov balíka Office prichádza v dvoch generáciách. Štandardné šifrovanie (Standard Encryption), ktoré zapisuje HotXLS, používa 128-bitové šifrovanie AES s odvodzovaním kľúča pomocou SHA-1. Flexibilné šifrovanie (Agile Encryption) prišlo neskôr a prechádza na AES-256 s SHA-512 a odlišným kľúčovým kontajnerom popísaným v XML. Obe sa v Exceli otvárajú transparentne a AES-128 je stále kryptograficky bezpečné na ochranu súborov pri prenose k zákazníkovi.

Rozdiel prestáva byť akademický v deň, keď sa v bezpečnostnom dotazníku objaví požiadavka na „šifrovanie súborov pomocou AES-256 pri nečinnosti“ (encryption at rest). Štandardné šifrovanie túto požiadavku nespĺňa bez ohlladu na silu hesla a žiadny parameter metódy SaveAsEncrypted nemení emitovaný algoritmus. Vo svojej bezpečnostnej dokumentácii preto presne uveďte profil: AES-128, ECMA-376 Standard Encryption, odvodenie kľúča SHA-1 s 50 000 iteráciami. Tvrdenie, ktoré prejde kontrolou, má väčšiu hodnotu ako optimistické, ktoré zlyhá pri audite.

Historická cesta XLS: RC4 na výstupe, RC4 a XOR na vstupe

Rozhranie BIFF má opačný charakter. Jeho šifrovanie je staršie a slabšie, no obojsmerný cyklus je úplný: to, čo zapíše, dokáže aj prečítať späť. Nastavením vlastnosti EncryptionPassword pred volaním SaveAs vytvoríte súbor .xls zašifrovaný pomocou RC4 cez mechanizmus BIFF FilePass. Metóda Open s parametrom hesla dokáže prečítať všetky tri staršie schémy: RC4, RC4 CryptoAPI aj starobylú obfuskáciu 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;

Šifra RC4 je dnes už zastaraná a nikdy by nemala chrániť dáta, na ktorých záleží. Jej jedinou zostávajoucou hodnotou je interoperabilita so systémami, ktoré si stále vymieňajú súbory .xls. Čítacia strana však má svoje uplatnenie pri migrácii. Heslom chránený starší súbor sa otvorí pomocou Open(FileName, Password), premostí sa do modelu OOXML a znova sa zabezpečí pomocou cesty AES, čo je jednosmerný upgrade, ktorý prebieha bez potreby prítomnosti Excelu. Pri veľkých objemoch šifrovaných doručení sa poznámky k priepustnosti pri ukladaní v našom článku o zápise prúdu dát pre dávkové úlohy servera vzťahujú na fázu vytvárania obsahu, ktorá prebieha pred šifrovaním.

Šifrovanie a ochrana si nekonkurujú

Ešte jeden bod stojí za vyjasnenie, pretože sa objaví vždy, keď niekto pochopí varovanie na začiatku tejto stránky tak, že „ochrana nemá žiadnu cenu“. Ochrana cenu má. Šifrovanie a ochrana odpovedajú na rôzne otázky a dajú sa kombinovať. Šifrovanie určuje, kto môže súbor otvoriť. Ochrana definuje, čo môže čitateľ, ktorý je už vnútri, zmeniť. Pri doručovaní miezd má zmysel urobiť oboje: zašifrovať balík tak, aby ho videl iba držiteľ hesla, a potom uzamknúť bunky so vzorcami, aby príjemca mohol filtrovať a triediť, ale nie prepisovať výpočty. Ochrana nie je chyba. Chybou je zamieňať si ochranu so šifrovaním, keď sa vyžaduje dôvernosť.

V správe kľúčov neexistuje žiadna záchranná sieť, a to zámerne. 50 000 iterácií odvodzovania kľúča slúži na to, aby bolo hádanie hesla náročné, a nič v súbore neumožňuje obnovu tajného kľúča. Stratené heslo znamená stratené dáta. Generujte, odovzdávajte a ukladajte tieto heslá s rovnakou disciplínou, akú uplatňujete pri prístupových údajoch k databázam, a šifrovanie splní svoju úlohu.

Skutočné šifrovanie súborov je v HotXLS záležitosťou jedného volania. Disciplína však spočíva vo všetkom okolo tohto volania: správa hesiel, hranica write-only zabraňujúca HotXLS opätovne otvoriť vlastný výstup a presná špecifikácia algoritmu, ktorú viete obhájiť pri audite. Metóda SaveAsEncrypted aj obojsmerná podpora pre staršie verzie sa dodávajú s komponentom HotXLS, pričom bežia natívne v procesoch Delphi a C++Builder bez akejkoľvek automatizácie Excelu.