Technický článek

Šifrování výstupu XLSX pomocí AES v Delphi přes HotXLS

Excel vystavuje dvě věci, kterým se říká „heslo", a jen jedna z nich je šifrování. Heslo pro otevření je klíčem ke skutečné šifře: bez něj se soubor vůbec nedá přečíst. Hesla pro ochranu listu a sešitu nedělají nic podobného. Nastaví příznak, který se spolupracující editor zavazuje respektovat, a sešit, který nese jen tento příznak, je obyčejný čitelný zip s daty ležícími v čistém textu. Vyberete-li to špatné, doručíte mzdový přehled, který v Excelu vypadá zamčeně a v jakémkoli textovém editoru se čte úplně normálně

Důkaz zabere deset vteřin. Přejmenujte chráněný .xlsx na .zip, otevřete ho v libovolném archivačním nástroji a podívejte se na xl/worksheets/sheet1.xml. Pokud jsou tam hodnoty buněk v čistém UTF-8, soubor není šifrovaný, ať Excel při pokusu upravit buňku vyvolá sebevíc dotazů na heslo. Tato mezera přežívá roky v týmech, které předpokládají, že ochrana listu je důvěrnost, a obvykle vyplave na povrch v den, kdy bezpečnostní kontrola provede přesně tohle přejmenování

HotXLS je nativní knihovna pro Delphi a C++Builder pro práci s tabulkovými procesory a tyto dvě funkce drží na opačných stranách této hranice. Ochrana listu a sešitu jsou editační omezení podložená záměrně slabým starším hashem. SaveAsEncrypted vytvoří balíček šifrovaný AES, který neotevře nic kromě hesla. Následující sekce popisují, co toto volání zapisuje, asymetrii, se kterou musíte v architektuře počítat (HotXLS šifrované soubory zapisuje, ale neumí je zpět přečíst), a v čem se liší starší cesta XLS

Diagram kontrastující ochranu listu XLSX v Delphi ukládající slabý hash a nechávající data buněk čitelná v obyčejném zipu s HotXLS SaveAsEncrypted odvozujícím klíč AES-128 a zapisujícím kontejner šifrování OLE
Ochrana listu ukládá slabý hash a nechává balíček čitelným zipem. SaveAsEncrypted odvodí klíč AES-128 a zapíše OLE kontejner, který žádný archivní nástroj nevypíše

Proč ochrana listu není šifrování

Metody Protect na listech a ProtectWorkbook na sešitu ukládají 4místný hexadecimální hash hesla. To je starší algoritmus, který OOXML i BIFF zdědily z Excelu devadesátých let, a dokumentace formátu nikdy netvrdí, že dělá víc, než že zastavuje náhodné úpravy. Balíček zůstává obyčejným čitelným zipem: data buněk, vzorce i sdílené řetězce, vše v čistém XML. Výchozí nastavení to ještě zhoršuje, ne zlepšuje. Každá buňka začíná s Locked=True, takže zavolání Protect bez předchozího odemknutí vstupního rozsahu zamrazí celý list proti editaci, zatímco každá hodnota zůstává na očích

Nic z toho ale nedělá ochranu zbytečnou. Nasměrování uživatelů do editovatelných rozsahů a stabilizace rozvržení pro tisk jsou reálné úkoly, popsané v našem článku o ochraně listu a nastavení stránky. Jenže to jsou úkoly týkající se použitelnosti. Ve chvíli, kdy je požadavkem důvěrnost, jediné API, které na ni odpovídá, je SaveAsEncrypted

Co SaveAsEncrypted skutečně zapisuje

Implementace se řídí ECMA-376 Standard Encryption, specifikovaným v [MS-OFFCRYPTO] sekci 2.3.4. Heslo projde 50 000 iteracemi SHA-1, aby se z něj odvodil klíč AES-128. Ověřovací blok, zašifrovaný pomocí AES-128 v režimu ECB, umožňuje konzumentovi potvrdit heslo dřív, než cokoli dešifruje, a celý balíček sešitu je pak zašifrován pomocí AES-128 v režimu CBC. Na disk nedopadne žádný zip. Je to soubor typu OLE compound file obsahující streamy EncryptionInfo, EncryptedPackage a DataSpaces, bez adresáře xl/, který by mohl archivační nástroj vypsat, a proto test s přejmenováním teď nenajde nic čitelného. Excel 2007 a novější ho otevře jen s heslem, a i aktuální LibreOffice umí Standard Encryption přečíst

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;

Zacházejte s proměnnou hesla se stejnou péčí jako s connection stringem. Získejte ho z trezoru nebo služby pro generovaná tajemství až na poslední chvíli, nikdy ho nelogujte a nikdy ho nezapisujte do samotného sešitu. Kontrola návratového kódu není zbytná ceremonie navíc. Šifrované uložení, které selže uprostřed, musí doručení přerušit, protože jediný náhradní plán, který volající kód nabídne, je nešifrovaná kopie, a přesně té kopii má tato funkce zabránit

Existuje také strojově ověřitelný akceptační test, který stojí téměř nic: zavolejte CanReadEncrypted na soubor, který jste právě zapsali. Vrátí true jen tehdy, když je výstup skutečně šifrovacím kontejnerem, takže jeho ověření po každém šifrovaném uložení zachytí nejdůležitější regresi, kód, který se potichu vrátil k obyčejnému SaveAs, přesně ve chvíli, kdy se to stane, ne o týdny později v zákazníkově schránce. Poslední slovo pořád patří ručnímu otevření v Excelu se skutečným heslem během testování před vydáním

Záměrně jen pro zápis: ošetření EXlsxEncryptionNotImplemented

Tady je asymetrie, která by měla utvářet architekturu vašeho pipeline: HotXLS při ukládání šifruje, ale při otevírání nedešifruje. OpenEncrypted vyvolá EXlsxEncryptionNotImplemented, když je namířeno na skutečně šifrovaný balíček; u obyčejného sešitu prostě propadne do normálního Open. Doprovodná sonda CanReadEncrypted levně detekuje šifrovací kontejner OLE, takže vstupní kód může takové soubory směrovat, aniž by výjimku vůbec vyvolal:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.CanReadEncrypted(FileName) then
    begin
      // Šifrovaný kontejner: HotXLS ho nedokáže dešifrovat.
      Writeln(FileName + ': needs manual decryption in Excel first');
      Exit;
    end;
    try
      Book.OpenEncrypted(FileName, '');   // nešifrované soubory propadnou do 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;

Tato asymetrie má jeden jasný architektonický výklad: šifrujte až na doručovací hraně, jako poslední krok. Nešifrovaný master držte uvnitř své důvěryhodné hranice, v databázi, dokumentovém úložišti nebo sdílení s řízeným přístupem, a šifrovanou kopii vyprodukujte jako poslední krok předtím, než soubor opustí systém. Pipeline, který archivuje jen šifrovaný výstup, se sám vyzamkl ze svých vlastních dat, protože žádná pozdější fáze téhož systému ty soubory nedokáže znovu otevřít. Když navazující proces HotXLS workbook znovu potřebuje, předejte mu nešifrovaný master, nikdy doručovací artefakt

Diagram pipeline volání HotXLS SaveAsEncrypted v Delphi: heslo trezoru propuštěné 50 000 koly SHA-1 do klíče AES-128, verifier blok ECB a šifrování balíčku CBC, produkující sloučený soubor OLE kontrolovaný přes CanReadEncrypted
Jedno volání SaveAsEncrypted promění trezorové heslo v klíč AES-128 a OLE compound file. CanReadEncrypted dává uložení akceptační bránu ověřitelnou strojem

AES-128 Standard Encryption a hranice compliance AES-256

Šifrování souborů Office přichází ve dvou generacích. Standard Encryption, ta, kterou zapisuje HotXLS, používá AES-128 s odvozením klíče přes SHA-1. Agile Encryption přišlo později a přechází na AES-256 se SHA-512 a jiným, XML popsaným kontejnerem klíčů. Obě se v Excelu otevřou transparentně, a AES-128 je stále výpočetně dostatečně silné pro ochranu souboru na cestě k zákazníkovi

Architektonický diagram pro služby Delphi: master sešitu v otevřeném textu zůstává uvnitř hranice důvěry, HotXLS SaveAsEncrypted běží jako poslední krok na doručovací hraně a varování před archivací jen zašifrované kopie
Šifrujte na doručovací hraně, naposledy, z hlavní kopie v plaintextu držené uvnitř hranice důvěry. Archivace jen zašifrované kopie zamkne každou pozdější fázi mimo její vlastní data

Rozdíl přestává být akademický v den, kdy bezpečnostní dotazník požaduje „šifrování souborů v klidu AES-256". Standard Encryption tuto hranici nesplňuje, bez ohledu na to, jak silné je heslo, a žádný parametr SaveAsEncrypted algoritmus, který produkuje, nezmění. Uveďte tedy ve své bezpečnostní dokumentaci profil přesně: AES-128, ECMA-376 Standard Encryption, odvození klíče SHA-1 s 50 000 iteracemi. Tvrzení, které přežije revizi, má větší cenu než optimistické tvrzení, které se zhroutí při auditu

Starší cesta XLS: RC4 ven, RC4 a XOR zase zpátky

Fasáda BIFF má opačný tvar. Její šifrování je starší a slabší, ale round-trip je úplný: co zapíše, dokáže i zpátky přečíst. Nastavení EncryptionPassword před SaveAs vyprodukuje přes mechanismus BIFF FilePass soubor .xls šifrovaný RC4, a Open s parametrem hesla přečte všechna tři starší schémata: RC4, RC4 CryptoAPI a prastarou obfuskaci XOR:

var
  Writer, Reader: IXLSWorkbook;   // reference přes rozhraní: bez ručního 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);  // Položky jsou indexovány od 1
end;

RC4 je zastaralá kryptografie a nikdy by neměla chránit data, na kterých dnes záleží; jedinou zbývající hodnotou je interoperabilita se systémy, které si stále vyměňují .xls. Čtecí strana si ale svou existenci zaslouží v migrační práci. Heslem chráněný starší soubor se otevře pomocí Open(FileName, Password), přemostí se do modelu OOXML a znovu zabezpečí cestou AES, jednosměrný upgrade, který proběhne bez Excelu kdekoli ve smyčce. Pro hromadné šifrované doručování platí poznámky o propustnosti na straně ukládání z našeho článku o streamovaném zápisu pro dávkové úlohy na serveru na fázi tvorby obsahu, která probíhá před šifrováním

Šifrování a ochrana nejsou soupeři

Ještě jeden bod stojí za ujasnění, protože vyvstane ve chvíli, kdy si někdo varování na začátku této stránky vyloží jako „ochrana je k ničemu". Není. Šifrování a ochrana odpovídají na jiné otázky a čistě se skládají na sebe. Šifrování rozhoduje, kdo může soubor otevřít; ochrana rozhoduje, co smí změnit čtenář, který už uvnitř je. Doručení mzdového přehledu může rozumně dělat obojí: zašifrovat balíček, aby ho viděl jen držitel hesla, a pak zamknout buňky se vzorci, aby příjemce mohl filtrovat a řadit, ale ne potichu přepisovat výpočty. Chybou nikdy není přidat ochranu. Chybou je nechat její přítomnost zastupovat šifrování ve chvíli, kdy byl požadavkem důvěrnost

Strana péče o hesla nemá žádnou záchrannou síť, a je to tak záměrně. Odvození klíče s 50 000 iteracemi existuje proto, aby bylo hádání drahé, a nic uvnitř souboru tajemství neuchovává v úschově. Ztracené heslo znamená ztracená data. Generujte, doručujte a ukládejte tato hesla se stejnou disciplínou, jakou uplatňujete u databázových přihlašovacích údajů, a šifrování dodrží svou část dohody

Skutečné šifrování souboru je v HotXLS jedno volání. Disciplína žije ve všem kolem tohoto volání: péče o heslo, hranice jen pro zápis, která brání HotXLS znovu otevřít svůj vlastní výstup, a tvrzení o algoritmu, které dokážete obhájit při auditu. SaveAsEncrypted a starší round-trip jsou součástí HotXLS Delphi Component, běžícího nativně v procesech Delphi a C++Builder bez jakékoli automatizace Excelu kdekoli na cestě