Artigo Técnico

Cifrar a Saída XLSX com AES em Delphi: o que o método SaveAsEncrypted do HotXLS Grava

O Excel disponibiliza duas opções distintas sob a designação "palavra-passe" (password), mas apenas uma delas constitui cifragem real. A palavra-passe de abertura (open password) ativa uma cifra real: sem ela, o ficheiro torna-se ilegível. As palavras-passe de proteção de folhas e livros não realizam esta operação: definem apenas um marcador (flag) que as aplicações de edição concordam em respeitar; contudo, um livro que contenha apenas esse marcador continua a ser um arquivo zip comum com dados legíveis em texto simples. Se escolher a opção incorreta, exportará dados confidenciais (como processamento de salários) que parecem bloqueados no Excel mas são legíveis em qualquer editor de texto

A confirmação requer dez segundos: altere a extensão de um ficheiro .xlsx protegido para .zip, abra-o numa ferramenta de compressão e verifique o ficheiro xl/worksheets/sheet1.xml. Se os dados das células estiverem visíveis em formato UTF-8 simples, o ficheiro não está cifrado, independentemente do número de avisos de palavra-passe que o Excel exiba na edição. Esta falha de segurança mantém-se ativa durante anos em equipas que assumem a proteção de folhas como garantia de confidencialidade, e costuma manifestar-se no dia em que uma auditoria de segurança realiza esta simples alteração de extensão

O HotXLS é uma biblioteca nativa Delphi e C++Builder para folhas de cálculo, que trata estas duas funcionalidades de forma distinta. A proteção de folhas e de livros constitui restrições de edição suportadas por um hash legado propositadamente fraco. Por outro lado, o método SaveAsEncrypted gera um pacote cifrado via AES que apenas a palavra-passe correta conseguirá abrir. As secções abaixo detalham o que é gravado nesta chamada, a assimetria que deve prever no desenho da aplicação (o HotXLS escreve ficheiros cifrados mas não os consegue ler de volta) e as diferenças na interface XLS legada

Por que a proteção de folhas não constitui cifragem

Os métodos Protect nas folhas e ProtectWorkbook no livro guardam um hash de 4 dígitos hexadecimais da palavra-passe. Este é o algoritmo legado herdado do Excel da década de 1990 pelos formatos OOXML e BIFF, e a especificação do formato nunca declara que este faça mais do que evitar edições acidentais. O pacote mantém-se como um arquivo zip comum: dados das células, fórmulas e strings partilhadas residem em XML de texto simples. A configuração padrão agrava esta situação: cada célula é inicializada com Locked=True; como tal, invocar Protect sem desbloquear previamente as áreas de dados bloqueia toda a folha para escrita enquanto exibe a informação em texto limpo

Isto não significa que a proteção seja inútil: orientar os utilizadores para os intervalos editáveis e estabilizar a estrutura para impressão são tarefas reais, detalhadas no nosso artigo sobre proteção de folhas e configuração de página. Contudo, são tarefas de usabilidade. No momento em que a confidencialidade for um requisito, a única API indicada é SaveAsEncrypted

O que o SaveAsEncrypted realmente grava

A implementação segue a cifragem padrão ECMA-376 (Standard Encryption), especificada na secção 2.3.4 de [MS-OFFCRYPTO]. A palavra-passe é submetida a 50.000 iterações SHA-1 para gerar a chave AES-128. Um bloco de validação (verifier block), cifrado com AES-128 em modo ECB, permite ao leitor validar a palavra-passe antes de iniciar a decifragem, sendo todo o pacote do livro cifrado com AES-128 em modo CBC. O resultado gravado no disco não é um ficheiro zip comum, mas sim um ficheiro composto OLE (OLE compound file) contendo os fluxos EncryptionInfo, EncryptedPackage e DataSpaces, sem qualquer diretório xl/ visível em ferramentas de compressão (pelo que o teste de alteração de extensão não exibirá dados legíveis). O Excel 2007 (e posterior) abre este ficheiro diretamente com a palavra-passe, bem como as versões recentes do LibreOffice

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;

Trate a variável da palavra-passe com o mesmo cuidado que dedicaria a uma string de ligação. Obtenha-a de um cofre de chaves (vault) ou de um gerador de credenciais no último momento, evite registá-la em logs e nunca a guarde no próprio livro de cálculo. A validação do código de retorno é obrigatória: uma gravação com cifragem que falhe a meio do processo deve interromper o fluxo de distribuição, uma vez que a única alternativa de recurso do código seria guardar uma cópia não cifrada, que constitui o incidente exato que esta funcionalidade visa evitar

Há também um teste de aceitação automático com custo negligenciável: invoque o método CanReadEncrypted sobre o ficheiro gravado. Este devolve verdadeiro apenas se a saída for um contentor cifrado real; assim, executar esta validação após cada gravação deteta regressões em que o código reverta silenciosamente para um método SaveAs comum, detetando a falha no imediato em vez de semanas mais tarde na receção pelo cliente. A validação final continua a ser a abertura manual no Excel com a palavra-passe real durante a fase de testes de lançamento

Escrita exclusiva por definição: gerir a exceção EXlsxEncryptionNotImplemented

A assimetria no comportamento dita o desenho do fluxo de processamento: o HotXLS cifra na gravação mas não realiza a decifragem na abertura. O método OpenEncrypted gera a exceção EXlsxEncryptionNotImplemented quando acede a um pacote cifrado real; num livro plano comum, avança de forma direta para o método Open. A validação CanReadEncrypted identifica o contentor de cifragem OLE com baixo custo, permitindo ao código de receção encaminhar estes ficheiros sem gerar exceções:

var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    if Book.CanReadEncrypted(FileName) then
    begin
      // Contentor cifrado: o HotXLS não o consegue decifrar.
      Writeln(FileName + ': needs manual decryption in Excel first');
      Exit;
    end;
    try
      Book.OpenEncrypted(FileName, '');   // ficheiros planos avançam para Open
      Writeln(FileName + ': opened, ' + IntToStr(Book.Sheets.Count) + ' sheet(s)');
    except
      on EXlsxEncryptionNotImplemented do
        Writeln(FileName + ': cifrado - encaminhado para fila manual');
    end;
  finally
    Book.Free;
  end;
end;

Esta assimetria dita uma lógica de desenho clara: a cifragem deve ser aplicada no último ponto do fluxo de distribuição. Mantenha a versão original em texto limpo dentro da sua zona de confiança (na base de dados, num repositório de documentos ou numa partilha segura) e gere a cópia cifrada como passo final antes de o ficheiro sair do sistema. Um fluxo que arquive exclusivamente a saída cifrada bloqueará o acesso aos seus próprios dados, pois nenhum processo posterior do sistema conseguirá reabrir esses ficheiros. Quando um processo a jusante necessitar novamente do livro de cálculo, faculte-lhe a versão em texto limpo e nunca o ficheiro cifrado gerado para entrega

Cifragem Padrão AES-128 e o requisito de conformidade AES-256

A cifragem de ficheiros do Office divide-se em duas gerações. A Cifragem Padrão (Standard Encryption), adotada pelo HotXLS, utiliza AES-128 com derivação de chave SHA-1. A Cifragem Ágil (Agile Encryption) surgiu posteriormente e utiliza AES-256 com SHA-512 e uma estrutura de chaves definida em XML. Ambas são abertas diretamente no Excel, e o algoritmo AES-128 continua a ser seguro para proteger ficheiros em trânsito para o cliente

A diferença deixa de ser meramente teórica quando os requisitos de segurança exigem explicitamente "cifragem AES-256 para dados em repouso". A Cifragem Padrão não cumpre esta diretiva, independentemente da complexidade da palavra-passe, e nenhum parâmetro do método SaveAsEncrypted altera o algoritmo gerado. Como tal, defina explicitamente o perfil na documentação de segurança do seu projeto: AES-128, Cifragem Padrão ECMA-376, derivação de chaves SHA-1 com 50.000 iterações. Um perfil de segurança documentado e rigoroso é mais valioso do que uma declaração ambígua que falhe numa auditoria

O fluxo XLS legado: escrita em RC4, leitura de RC4 e XOR

A interface BIFF possui a estrutura oposta: a sua cifragem é antiga e menos segura, mas o fluxo de leitura e escrita é completo (o que ela escreve, consegue ler de volta). Definir a propriedade EncryptionPassword antes de SaveAs gera um ficheiro .xls cifrado com RC4 através do registo BIFF FilePass, e o método Open com o argumento de palavra-passe lê os três esquemas legados (RC4, RC4 CryptoAPI e a antiga ofuscação XOR):

var
  Writer, Reader: IXLSWorkbook;   // referências de interface: sem Free manual
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);  // as entradas baseiam-se em 1
end;

O RC4 é um algoritmo obsoleto e não deve ser utilizado para proteger informação sensível nos dias de hoje; o seu único interesse reside na interoperabilidade com sistemas que ainda exijam o formato .xls. A vertente de leitura, contudo, é útil em tarefas de migração: um ficheiro legado protegido com palavra-passe é aberto com Open(FileName, Password), convertido para o modelo OOXML e cifrado no novo padrão AES, constituindo uma atualização de segurança direta sem necessidade de recorrer ao Excel. Para distribuição de grandes volumes de ficheiros cifrados, as notas de desempenho de escrita abordadas no nosso artigo sobre escrita por streaming para processamento em lote aplicam-se à fase de montagem dos dados que precede a cifragem

A cifragem e a proteção não são concorrentes

Um aspeto relevante a esclarecer: a proteção não é inútil. A cifragem e a proteção resolvem problemas distintos e complementam-se. A cifragem determina quem consegue abrir o ficheiro; a proteção define o que o utilizador autorizado pode alterar no seu interior. A distribuição de dados confidenciais (como relatórios de salários) pode e deve combinar ambas: cifre o ficheiro para garantir que apenas o utilizador com a palavra-passe aceda à informação e bloqueie as células de fórmulas para permitir ordenar os dados mas evitar alterações nos cálculos. O erro não reside em aplicar a proteção, mas sim em confiar apenas nela quando a confidencialidade é o requisito obrigatório

A gestão das palavras-passe não tem margem para erro por desenho: a derivação da chave com 50.000 iterações visa dificultar a adivinhação e não existem chaves de recuperação no ficheiro. A perda da palavra-passe dita a perda irreversível dos dados. Gere, envie e guarde estas chaves com o mesmo rigor que dedicaria a credenciais de base de dados para garantir a integridade do processo

A cifragem real de ficheiros requer apenas uma chamada de método no HotXLS. O rigor reside nas tarefas envolventes: a custódia das palavras-passe, a assimetria que impede a reabertura do ficheiro cifrado e a especificação clara do perfil criptográfico. O método SaveAsEncrypted e o processamento XLS legado são fornecidos com o HotXLS Component, correndo de forma nativa em processos Delphi e C++Builder sem recurso à automação do Excel