Um backup restaurado contém um registro JSON criptografado para o user-1842; no entanto, a aplicação o aceita mesmo após um atacante alterar os bytes armazenados e fazer com que a role (função/perfil) recuperada passe a ser admin. O atacante não precisou da chave de criptografia nem de acesso à API. Bastou ter acesso de escrita ao backup do banco de dados ou aos seus metadados. O artefato em execução aqui é um módulo Python que protege esse registro — contendo id, email, role e created_at — à medida que ele transita entre um cliente, uma API, um banco de dados e um backup. A criptografia simétrica utiliza a mesma chave secreta tanto para transformar texto simples em texto cifrado quanto para recuperar o texto simples posteriormente. Ela oculta o registro, mas o simples fato de ocultá-lo não torna confiáveis os bytes que foram alterados.
A questão que se coloca para quem defende o sistema é, portanto, mais específica do que simplesmente saber qual algoritmo de cifragem tem o nome mais robusto. Quando o valor role=admin retorna do armazenamento, como a API pode provar que esses bytes — e o contexto do registro a que pertencem — são exatamente os mesmos que foram protegidos originalmente? Os limites definidos para o user-1842 indicam onde o texto simples deve terminar, onde a chave deve permanecer isolada e em que pontos um atacante pode alterar o texto cifrado sem descobrir o seu conteúdo. Vamos acompanhar a trajetória desse registro através desses limites:
Este diagrama mostra que o banco de dados e o backup precisam do texto cifrado, de um nonce e de metadados selecionados, mas nunca da chave de criptografia. Um nonce é um valor usado apenas uma vez com uma determinada chave para garantir que operações de proteção repetidas não reutilizem as mesmas entradas criptográficas. O nonce pode ser armazenado junto ao texto cifrado; manter o sigilo não é a sua função. A chave deve permanecer secreta e deve vir de uma configuração protegida da aplicação, e não do registro ou do backup.
Como um atacante pode alterar os bytes armazenados, o contrato do registro exige confidencialidade e integridade simultâneas. A criptografia autenticada cifra os bytes JSON e verifica se nem esses bytes nem o contexto selecionado foram alterados antes de retornar o texto simples. Para user-1842, a API autenticará user_id=user-1842 e schema_version=1 como dados associados — ou seja, metadados verificados pela operação criptográfica, mas que permanecem sem cifragem. Assim, um texto cifrado copiado não pode ser silenciosamente reclassificado como o registro de outro usuário, e um valor de esquema não pode ser alterado sem causar falha na verificação.
O texto simples expõe diretamente o e-mail e a função (role). AES-CTR e AES-CBC podem ocultar esses campos, mas nenhum deles rejeita modificações de forma independente. O AES-GCM fornece o contrato de criptografia autenticada necessário quando utilizado com um novo nonce para cada cifragem sob uma mesma chave. Um modo de apenas cifragem consegue detectar se um byte do backup foi alterado antes de a API processar o campo role? A comparação a seguir avalia as opções de armazenamento em relação a esse requisito:
Essa comparação torna concreta a decisão de projeto. O AES-GCM rejeita um texto cifrado, nonce, chave ou dados associados que tenham sido modificados, indicando uma falha de autenticação, enquanto opções que oferecem apenas confidencialidade deixam a aplicação sem essa garantia. O AES-GCM não corrige a reutilização de nonces. Reutilizar um nonce com a mesma chave compromete a segurança; portanto, a geração do nonce deve ocorrer dentro do fluxo de proteção, em vez de ser um metadado fornecido por quem chama a função.
Regra para o registro Armazene o nonce, o texto cifrado e os dados associados juntamente com o registro. Armazene a chave AES separadamente e garanta que cada nonce seja único para uma determinada chave.
Com o contrato definido, o módulo pode proteger user-1842 sem expor um registro parcialmente verificado ao chamador. O serializador JSON produz bytes, pois a função AESGCM.encrypt opera sobre bytes. Para este exemplo, o módulo gera uma chave AES de 256 bits, cria um novo nonce de 12 bytes e armazena um pacote no formato {nonce, texto cifrado, ad}. Em um ambiente de produção, a geração e a custódia da chave são transferidas para o limite de gerenciamento de chaves aprovado da aplicação, enquanto o pacote do registro permanece seguro para armazenamento em bancos de dados e backups.
Aqui está o fluxo completo e mais conciso de proteção e recuperação: