Artigo Técnico

Proteção de planilha XLSX no Delphi: 15 opções Allow

Você entrega uma pasta de trabalho pronta a um colega e pede que ele filtre o conteúdo, não que o reescreva. Então você protege a planilha. Nas versões antigas do HotXLS, esse gesto gravava uma única coisa no arquivo: <sheetProtection sheet="1" objects="1" scenarios="1"/>, fixo, sempre. A planilha era bloqueada, o hash da senha era anexado e o usuário não podia fazer mais nada, nem mesmo a ordenação e o filtro que você realmente queria deixar abertos. A caixa de diálogo "Proteger Planilha" do próprio Excel tem quinze caixas de seleção exatamente por esse motivo, e o mecanismo não conseguia expressar nenhuma delas. É essa lacuna que o modelo de proteção da v2.91.0 fecha

HotXLS é um componente nativo VCL de planilhas para Delphi e C++Builder que lê e grava XLS e XLSX sem precisar do Excel instalado. Este artigo trata do lado XLSX da proteção de planilhas: o novo enum TXLSXSheetProtectionOption, a propriedade AllowOption que alterna cada permissão e a única regra de codificação OOXML que sempre pega quem escreve um elemento <sheetProtection> à mão

O que a proteção da planilha realmente controla

Primeiro, o limite, porque ele determina o quanto você deve confiar em tudo isso. A proteção de planilha no formato de planilha OOXML (ECMA-376) é uma política de interação, não criptografia. Ela diz a um aplicativo compatível quais edições devem ser recusadas enquanto a planilha está protegida. Os valores das células continuam em xl/worksheets/sheetN.xml em texto puro; basta abrir o .xlsx e eles estão lá. A senha opcional é armazenada como um hash antigo e curto, não como uma chave que embaralha os dados. Qualquer pessoa que renomeie o arquivo, abra a parte e remova a linha <sheetProtection> lê e edita tudo

Então a proteção responde "impedir que meu colega apague uma fórmula por engano", não "manter esses dados secretos de alguém determinado". São problemas diferentes, com ferramentas diferentes. Se você precisa de confidencialidade, quer a criptografia no nível da pasta de trabalho coberta em saída XLSX protegida por AES, que de fato cifra o pacote. Proteção de planilha e criptografia de pasta de trabalho se combinam bem, mas só a segunda é um cadeado. Mantenha essa distinção clara e o resto da página vira apenas encanamento

As 15 opções e a propriedade AllowOption

Cada planilha agora carrega um conjunto de valores TXLSXSheetProtectionOption que descrevem o que o usuário ainda pode fazer enquanto a planilha está protegida. Os membros se mapeiam um a um para os atributos OOXML e para as caixas de seleção da caixa de diálogo do Excel:

  • xlsxSpoEditObjects, xlsxSpoEditScenarios - editar objetos de desenho e cenários hipotéticos
  • xlsxSpoFormatCells, xlsxSpoFormatColumns, xlsxSpoFormatRows - reformatar células, colunas e linhas
  • xlsxSpoInsertColumns, xlsxSpoInsertRows, xlsxSpoInsertHyperlinks - inserir colunas, linhas e links
  • xlsxSpoDeleteColumns, xlsxSpoDeleteRows - excluir colunas e linhas
  • xlsxSpoSelectLockedCells, xlsxSpoSelectUnlockedCells - mover a seleção para células bloqueadas ou desbloqueadas
  • xlsxSpoSort, xlsxSpoAutoFilter, xlsxSpoPivotTables - ordenar intervalos, usar menus do AutoFilter e trabalhar com PivotTables

Você lê e grava bits individuais pela propriedade indexada AllowOption em TXLSXWorksheet. AllowOption[Opt] = True significa que a ação é permitida; definir False a proíbe. O conjunto inteiro também pode ser acessado de uma vez por SheetProtectionOptions, um TXLSXSheetProtectionOptions (um simples set of Pascal), então você pode salvá-lo, restaurá-lo ou substituí-lo por completo

O padrão importa e é intencional: uma worksheet recém-criada começa com todas as opções permitidas. O construtor preenche SheetProtectionOptions com o intervalo inteiro, [Low(TXLSXSheetProtectionOption)..High(TXLSXSheetProtectionOption)]. Você restringe a partir daí excluindo as ações que quer proibir, em vez de montar um conjunto de permissões do zero. É essa escolha que faz a regra de codificação do escritor, abaixo, coincidir com o comportamento do Excel

Proteger a planilha mantendo ordenação e filtro liberados

Este é o caso comum de ponta a ponta: proteger um relatório pronto para que o layout não seja remexido, mas deixar o leitor ordená-lo e filtrá-lo. Observe que Protect e as opções são independentes. Protect coloca a planilha no estado protegido e armazena o hash opcional da senha; ele não mexe no conjunto de opções. Você ajusta AllowOption separadamente, e as alternâncias entram em vigor quando a planilha é protegida e salva

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    sh := wb.Sheets.Add('Protected');
    sh.Cells[1, 1].Value := 'Region'; sh.Cells[1, 2].Value := 'Units';
    sh.Cells[2, 1].Value := 'North';  sh.Cells[2, 2].Value := 120;
    sh.Cells[3, 1].Value := 'South';  sh.Cells[3, 2].Value := 98;

    // Protect with a password. This only sets the protected state + hash;
    // the option set is left at its all-permitted default.
    sh.Protect('HotXLS-2026');

    // Narrow: keep sort + AutoFilter, forbid reshaping and reformatting.
    sh.AllowOption[xlsxSpoSort]          := True;
    sh.AllowOption[xlsxSpoAutoFilter]    := True;
    sh.AllowOption[xlsxSpoFormatCells]   := False;
    sh.AllowOption[xlsxSpoFormatColumns] := False;
    sh.AllowOption[xlsxSpoFormatRows]    := False;
    sh.AllowOption[xlsxSpoInsertRows]    := False;
    sh.AllowOption[xlsxSpoDeleteRows]    := False;

    if wb.SaveAs('protection.xlsx') <> 1 then
      Writeln('SaveAs failed');
  finally
    wb.Free;
  end;
end;

Há duas coisas a tirar desse trecho. As linhas Sort e AutoFilter são escritas explicitamente mesmo que ambas tenham padrão True; isso é documentação para quem vier depois, não requisito funcional. E, como os padrões são permissivos, as únicas linhas que alteram o arquivo de saída são as que colocam uma opção como False. Isso não é um acidente desta API, é o formato OOXML aparecendo na superfície, que é o próximo tópico

A regra de codificação: omitir libera, attr=0 bloqueia

Este é o único fato contraintuitivo de todo o recurso, e é onde o <sheetProtection> escrito à mão costuma errar. Em OOXML, cada atributo por ação é uma flag de proibição, e sua ausência significa permissão. Um atributo ausente quer dizer que a ação é permitida. Um atributo escrito como "0" quer dizer que a ação é proibida enquanto a planilha estiver protegida. Não existe formatCells="1" em um arquivo bem formado para significar "a formatação é permitida"; basta omitir o atributo. (O padrão de um atributo ausente é o booleano true do OOXML, e esses atributos são nomeados de forma que "true" signifique que a edição correspondente é permitida.)

O escritor do HotXLS espelha isso exatamente. Ele emite sheet="1" para ativar a proteção e então percorre o conjunto de opções escrevendo attr="0" apenas para as opções que você definiu como False. As ações permitidas não geram saída. Então a pasta de trabalho da seção anterior se serializa para algo assim, levando apenas as ações proibidas e o hash da senha:

// Conceptual output for the snippet above (attributes elided for brevity):
// <sheetProtection sheet="1"
//   formatCells="0" formatColumns="0" formatRows="0"
//   insertRows="0" deleteRows="0"
//   password="...4-hex..."/>
// Note what is NOT there: no sort, no autoFilter, no selectLockedCells.
// Their absence is exactly what tells Excel those actions stay allowed.

Se você veio da antiga string fixa e esperava ver cada atributo escrito por extenso, isso parece esparso, quase errado. Mas está certo. Um arquivo que listasse sort="1" e autoFilter="1" significaria a mesma coisa para um leitor compatível, mas o próprio Excel grava a forma mínima, só com proibições, e imitar isso mantém os diffs pequenos e as idas e voltas sem drama. Os atributos objects e scenarios seguem a mesma regra: eles são permitidos por padrão, então só aparecem como "0" quando você os proíbe, o oposto do antigo objects="1" scenarios="1" que era emitido sem condição

Ler a proteção de volta: fidelidade no ida e volta

Um modelo de permissões que você pode gravar mas não ler é uma via de mão única, e o sintoma normal é um ciclo carregar-editar-salvar que alarga permissões em silêncio. O HotXLS resolve isso. Quando ParseWorksheetXml encontra um elemento <sheetProtection>, ele marca a planilha como protegida, captura o hash da senha se houver e então decodifica cada atributo por ação de volta para AllowOption usando a mesma convenção ao contrário: um atributo presente e igual a "0" proíbe a ação; um atributo ausente deixa a opção no padrão permitido

var
  wb: TXLSXWorkbook;
  sh: TXLSXWorksheet;
begin
  wb := TXLSXWorkbook.Create;
  try
    wb.LoadFromFile('protection.xlsx');
    sh := wb.Sheets[1];                  // XLSX sheets are 1-based
    if sh.IsProtected then
    begin
      Writeln('Protected; password hash present: ',
        sh.SheetProtectHash <> '');
      Writeln('Sort allowed:       ', sh.AllowOption[xlsxSpoSort]);
      Writeln('AutoFilter allowed: ', sh.AllowOption[xlsxSpoAutoFilter]);
      Writeln('FormatCells allowed:', sh.AllowOption[xlsxSpoFormatCells]);
    end;
  finally
    wb.Free;
  end;
end;

Carregue o arquivo produzido pelo escritor e você recebe Sort e AutoFilter de volta como True, FormatCells como False - o conjunto que você salvou, intacto. Essa simetria é o objetivo inteiro: edite uma célula em uma planilha protegida com permissões parciais e salve de novo, e as catorze permissões que você não tocou sobrevivem em vez de colapsarem para o antigo padrão tudo ou nada

Notas práticas e limites

Algumas coisas valem saber antes de encaixar isso em um pipeline de relatórios:

  • A senha é fraca por design. A proteção de planilha XLSX armazena um hash legado de 16 bits, o mesmo que o Excel usa há décadas, mantido aqui por interoperabilidade. Ela desencoraja edições acidentais, mas não resiste a um atacante. Não a trate como guardiã de segredo. Para proteção real, criptografe a pasta de trabalho
  • Definir opções antes de proteger é tranquilo. AllowOption pode ser atribuído com a planilha protegida ou não; as alternâncias apenas descrevem o que a proteção permitirá quando Protect entrar em vigor. UnProtect limpa o estado protegido e o hash, mas deixa o conjunto de opções em vigor para a próxima vez
  • A semântica de célula bloqueada continua valendo. A proteção só bloqueia edições em células cujo atributo Locked está definido, o padrão da pasta de trabalho. Deixar uma área de entrada editável é tarefa do estilo da célula, não de uma opção de proteção; as duas camadas se combinam do mesmo jeito que no Excel
  • Este é o mecanismo XLSX. O modelo de opções espelha as antigas propriedades Allow* do mecanismo XLS, mas o enum e os nomes de propriedade aqui (xlsxSpo*, AllowOption) pertencem a TXLSXWorksheet em lxHandleX. Se você também controla o layout de impressão nas mesmas planilhas, o guia de proteção e configuração de página mostra como essas definições convivem com áreas de impressão e cabeçalhos, e validação de dados, AutoFilter e tabelas combina naturalmente com deixar xlsxSpoAutoFilter aberto em um relatório bloqueado

O modelo de proteção granular e o restante do mecanismo de leitura e gravação XLSX fazem parte do HotXLS Component para Delphi e C++Builder; a página do produto traz a API completa da worksheet, inclusive a referência integral das opções de proteção