Teknisk artikel

Kryptering af XLSX-output med AES i Delphi: Hvad HotXLS SaveAsEncrypted skriver

Excel eksponerer to ting, der begge kaldes "adgangskode", og kun den ene af dem er kryptering. Åbnings-adgangskoden aktiverer et rigtigt ciphersystem: Uden den kan filen overhovedet ikke læses. Adgangskoderne til beskyttelse af regneark og arbejdsbøger gør intet i den retning. De sætter et flag, som en samarbejdende editor indvilliger i at respektere, og en arbejdsbog, der kun bærer dette flag, er en helt almindelig, læsbar zip-fil, hvor dataene ligger i klartekst. Vælger du den forkerte, sender du lønoplysninger afsted, som ser låst ud i Excel, men kan læses i enhver teksteditor

Beviset tager ti sekunder. Omdøb en beskyttet .xlsx til .zip, open den i et hvilket som helst arkivværktøj, og kig på xl/worksheets/sheet1.xml. Hvis celleværdierne er der i almindelig UTF-8, er filen ikke krypteret, uanset hvor mange adgangskodeprompter Excel viser, når nogen forsøger at redigere en celle. Dette sikkerhedshul lever i bedste velgående i årevis i teams, der antager, at arkbeskyttelse er det samme som fortrolighed, og det kommer normalt for dagens lys den dag, en sikkerhedsgennemgang foretager præcis denne omdøbning

HotXLS er et indfødt Delphi- og C++Builder-regnearksbibliotek, og det holder de to funktioner på hver sin side af den linje. Beskyttelse af regneark og arbejdsbøger er redigeringsbegrænsninger understøttet af en bevidst svag, ældre hash-algoritme. SaveAsEncrypted producerer en AES-krypteret pakke, som absolut intet andet end adgangskoden kan åbne. Afsnittene nedenfor dækker, hvad dette kald skriver, den asymmetri du skal designe omkring (HotXLS skriver krypterede filer, men kan ikke læse dem tilbage), og hvordan den ældre XLS-sti adskiller sig

Hvorfor arkbeskyttelse ikke er kryptering

Metoderne Protect på ark og ProtectWorkbook på arbejdsbogen gemmer en 4-cifret hex-hash af adgangskoden. Det is den ældre algoritme, som både OOXML og BIFF arvede fra 1990'ernes Excel, og formatdokumentationen hævder aldrig, at den gør andet end at forhindre utilsigtede redigeringer. Pakken forbliver en almindelig, læsbar zip-fil: Celledata, formler og delte strenge er alle i klartekst-XML. Standarden gør det værre, ikke bedre. Hver eneste celle starter med Locked=True, så at kalde Protect uden først at låse et inputområde op, fryser hele arket mod redigering, mens enhver værdi efterlades fuldt synlig

Intet af det gør beskyttelse ubrugelig. At styre brugere ind i redigerbare områder og stabilisere et layout til udskrivning er reelle opgaver, som dækkes i vores artikel om regnearksbeskyttelse og sideopsætning. Men det er brugervenlighedsopgaver. I det øjeblik kravet er fortrolighed, er det eneste API, der opfylder det, SaveAsEncrypted

Hvad SaveAsEncrypted rent faktisk skriver

Implementeringen følger ECMA-376 Standard Encryption, som er specificeret i [MS-OFFCRYPTO] afsnit 2.3.4. Adgangskoden kører gennem 50.000 iterationer af SHA-1 for at udlede en AES-128-nøgle. En verifikatorblok, krypteret med AES-128 i ECB-tilstand, lader modtageren bekræfte adgangskoden, før den dekrypterer noget, og hele arbejdsbogspakken krypteres derefter med AES-128 i CBC-tilstand. Det, der lander på disken, er slet ikke en zip-fil. Det er en OLE compound-fil, der indeholder EncryptionInfo-, EncryptedPackage- og DataSpaces-strømme uden nogen xl/-mappe, som et arkivværktøj kan opliste, hvilket er grunden til, at omdøbningstesten nu ikke viser noget læsbart. Excel 2007 og senere åbner den med adgangskoden alene, og den nuværende LibreOffice læser også 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;

Behandl adgangskodevariablen med samme omhu som en forbindelsesstreng. Hent den fra en sikkerhedsboks (vault) eller en genereret hemmelig tjeneste i sidste øjeblik, log den aldrig, og skriv den aldrig ind i selve arbejdsbogen. Kontrollen af returkoden er ikke en valgfri formalitet. En krypteret lagring, der fejler undervejs, skal afbryde leveringen, da det eneste fallback, som den kaldende kode kan tilbyde, er en ukrypteret kopi, og denne kopi er netop den hændelse, som denne funktion eksisterer for at forhindre

Der findes også en maskinkontrollerbar accepttest, som næsten intet koster: Kald CanReadEncrypted på den fil, du lige har skrevet. Den returnerer kun true, når outputtet reelt er en krypteringsbeholder, så at tjekke dette efter hver krypteret lagring fanger den regression, der betyder mest — nemlig en kodesti, der stiltiende faldt tilbage på en almindelig SaveAs — i det øjeblik det sker, frem for uger senere i en kundes indbakke. Det sidste ord tilhører dog stadig en manuel åbning i Excel med den rigtige adgangskode under frigivelsestest

Skrivebeskyttet af design: Håndtering af EXlsxEncryptionNotImplemented

Her er den asymmetri, der bør forme din import-arkitektur: HotXLS krypterer ved lagring, men dekrypterer ikke ved åbning. OpenEncrypted udløser undtagelsen EXlsxEncryptionNotImplemented, når den peges på en faktisk krypteret pakke; på en almindelig arbejdsbog falder den blot igennem to en normal Open. Den ledsagende undersøgelse CanReadEncrypted detekterer billigt OLE-krypteringsbeholderen, så modtagelseskoden kan dirigere sådanne filer uden at udløse undtagelsen:

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;

Denne asymmetri har en klar arkitektonisk fortolkning: Krypter ved leveringsgrænsen, til sidst. Behold klartekst-masteren inden for din tillidsgrænse — i en database, et dokumentlager eller et adgangsstyret netværksdrev — og opret den krypterede kopi som det absolut sidste trin, før filen forlader systemet. En importpipeline, der kun arkiverer det krypterede output, har låst sig selv ude fra sine egne data, da intet senere trin i det samme system kan genåbne disse filer. Når en efterfølgende HotXLS-proces har brug for arbejdsbogen igen, skal du give den klartekst-masteren, aldrig leveringsartefakten

AES-128 Standard Encryption og grænsen for AES-256-overensstemmelse

Kryptering af Office-filer findes i to generationer. Standard Encryption, som er den HotXLS skriver, bruger AES-128 med SHA-1-nøgleudledning. Agile Encryption ankom senere og skifter til AES-256 med SHA-512 og en anden XML-beskrevet nøglebeholder. Begge åbnes transparent i Excel, og AES-128 er stadig beregningsmæssigt solid til at beskytte en fil under transport til en kunde

Forskellen holder op med at være akademisk den dag, et sikkerhedsspørgeskema beder om "AES-256-kryptering af inaktive filer (at rest)." Standard Encryption opfylder ikke dette krav, uanset hvor stærk adgangskoden er, og intet parameter i SaveAsEncrypted ændrer den algoritme, den udsender. Formuler derfor profilen præcist i din sikkerhedsdokumentation: AES-128, ECMA-376 Standard Encryption, SHA-1 nøgleudledning med 50.000 iterationer. En påstand, der overlever en revision, er mere værd end en optimistisk ene af slagsen, der falder sammen under et audit

Den ældre XLS-sti: RC4 ud, RC4 og XOR tilbage ind

Den ældre .xls-sti bruger RC4-kryptering, og dens regler er af en anden karakter. At angive EncryptionPassword før SaveAs producerer en RC4-krypteret .xls via BIFF FilePass-mekanismen, og Open med et adgangskodeparameter læser alle tre ældre skemaer: RC4, RC4 CryptoAPI og den urgamle XOR-obfuskering:

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 er forældet kryptografi og bør aldrig beskytte data, der betyder noget i dag; dens eneste tilbageværende værdi er interoperabilitet med systemer, der still udveksler .xls. Læsesiden gør dog god gavn i migrationsarbejde. En adgangskodebeskyttet ældre fil åbnes med Open(FileName, Password), brobygger ind i OOXML-modellen og gen-sikres via AES-stien — en envejsopgradering, der kører uden Excel overhovedet i løkken. For krypterede leverancer i store mængder gælder bemærkningerne om lagringskapacitet i vores artikel om streaming-skrivning til server-batch-job for den indholdsopbyggende fase, der sker før kryptering

Kryptering og beskyttelse er ikke rivaler

Endnu et punkt er værd at få på plads, da det opstår i det øjeblik, nogen læser advarslen øverst på denne side som "beskyttelse er værdiløs." Det er den ikke. Kryptering og beskyttelse besvarer forskellige spørgsmål, og de supplerer hinanden fint. Kryptering afgør, hvem der kan åbne filen; beskyttelse afgør, hvad en læser, der allerede er inde, må ændre. En lønudbetalingslevering kan med rimelighed gøre begge dele: Kryptere pakken, så kun ejeren af adgangskoden ser den, og derefter låse formelcellerne, så modtageren kan filtrere og sortere, men ikke stiltiende omskrive beregningerne. Fejlen er aldrig at tilføje beskyttelse. Fejlen er at lade dens tilstedeværelse træde i stedet for kryptering, når kravet var fortrolighed

Opbevaringssiden har intet sikkerhedsnet, og det er tilsigtet. Nøgleudledningen med 50.000 iterationer eksisterer for at gøre gætteri dyrt, og intet i filen deponerer (escrows) hemmeligheden. En tabt adgangskode er tabt data. Generer, lever og gem disse adgangskoder med samme disciplin, som du anvender på databaseoplysninger, og krypteringen vil holde sin del af aftalen

Rigtig filkryptering er ét kald i HotXLS. Deciplinen ligger i alt omkring kaldet: Opbevaring af adgangskoder, den skrivebeskyttede grænse, der forhindrer HotXLS i at genåbne sit eget output, og et algoritmekrav, du kan forsvare i en revision. SaveAsEncrypted og den ældre rundtursfunktion leveres med HotXLS Component, som kører indfødt i Delphi- og C++Builder-processer uden Excel-automatisering overhovedet på stien