Una copia de seguridad restaurada contiene un registro JSON cifrado para user-1842; sin embargo, la aplicación lo acepta después de que un atacante modifica los bytes almacenados y el campo role recuperado pasa a ser admin. El atacante no necesitó la clave de cifrado ni acceso a la API; solo requirió acceso de escritura a la copia de seguridad de la base de datos o a sus metadatos. Nuestro artefacto en ejecución es un módulo de Python que protege este registro —que contiene id, email, role y created_at— mientras este se desplaza entre un cliente, una API, una base de datos y una copia de seguridad. El cifrado simétrico utiliza la misma clave secreta para convertir texto plano en texto cifrado y para recuperar posteriormente el texto plano. Este método oculta el registro, pero el simple hecho de ocultarlo no garantiza la integridad de los bytes alterados.
Por tanto, la cuestión que se plantea el defensor es más específica que determinar qué algoritmo de cifrado tiene el nombre más sólido. Cuando el valor role=admin se recupera del almacenamiento, ¿cómo puede la API demostrar que dichos bytes y el contexto del registro al que pertenecen son exactamente los que se protegieron originalmente? Los límites establecidos para user-1842 indican dónde debe terminar el texto plano, dónde debe mantenerse aislada la clave y dónde un atacante puede alterar el texto cifrado sin llegar a conocer su contenido. Sigamos la trayectoria del registro a través de esos límites:
Este diagrama muestra que la base de datos y la copia de seguridad requieren el texto cifrado, un nonce y metadatos seleccionados, pero nunca la clave de cifrado. Un nonce es un valor que se utiliza una sola vez con una clave determinada para garantizar que las operaciones de protección repetidas no reutilicen las mismas entradas criptográficas. El nonce puede almacenarse junto al texto cifrado; su función no es mantener la confidencialidad. La clave debe permanecer secreta y provenir de una configuración de aplicación protegida, en lugar de extraerse del registro o de la copia de seguridad.
Dado que un atacante puede alterar los bytes almacenados, el contrato del registro exige tanto confidencialidad como integridad. El cifrado autenticado cifra los bytes JSON y verifica que ni dichos bytes ni el contexto seleccionado hayan cambiado antes de devolver el texto plano. Para user-1842, la API autenticará user_id=user-1842 y schema_version=1 como datos asociados; es decir, metadatos verificados por la operación criptográfica pero que permanecen sin cifrar. De este modo, no es posible reetiquetar silenciosamente un texto cifrado copiado como el registro de otro usuario, ni modificar un valor del esquema sin provocar un fallo en la verificación.
El texto plano deja expuestos directamente el correo electrónico y el rol. Aunque AES-CTR y AES-CBC pueden ocultar estos campos, ninguno de ellos detecta modificaciones por sí solo. AES-GCM proporciona el contrato de cifrado autenticado necesario cuando se utiliza un nonce nuevo para cada operación de cifrado con una misma clave. ¿Puede un modo de solo cifrado detectar si un byte de la copia de seguridad ha cambiado antes de que la API procese el campo role? La siguiente comparativa evalúa las opciones de almacenamiento en función de este requisito:
Esta comparación concreta la decisión de diseño. AES-GCM rechaza un texto cifrado, un nonce, una clave o datos asociados que hayan sido modificados, indicando un fallo de autenticación, mientras que las opciones que solo ofrecen confidencialidad dejan a la aplicación sin esa garantía. AES-GCM no soluciona los problemas derivados de la reutilización de nonces. Reutilizar un nonce con la misma clave compromete la seguridad; por ello, la generación del nonce debe formar parte del proceso de protección en lugar de tratarse como metadatos proporcionados por quien realiza la llamada.
Regla para el registro Almacene el nonce, el texto cifrado y los datos asociados junto con el registro. Almacene la clave AES por separado y asegúrese de que cada nonce sea único para cada clave.
Una vez establecido el contrato, el módulo puede proteger user-1842 sin exponer un registro parcialmente verificado a quien realiza la llamada. El serializador JSON genera bytes, ya que AESGCM.encrypt opera sobre bytes. Para este ejemplo, el módulo genera una clave AES de 256 bits, crea un nuevo nonce de 12 bytes y almacena un paquete con la estructura {nonce, texto cifrado, datos asociados}. En el entorno de despliegue, la generación y custodia de claves se trasladan al ámbito de gestión de claves aprobado por la aplicación, mientras que el paquete del registro puede almacenarse de forma segura en la base de datos y en las copias de seguridad.
A continuación se presenta el flujo completo más sencillo para la protección y recuperación: