AES (Advanced encryption standard) es un algoritmo bastante fuerte y seguro, utilizado ampliamente en el mundo de la seguridad informática.
MySQL permite cifrar y descifrar datos utilizando el algoritmo AES a través de las funciones aes_encrypt
y aes_decrypt
.
De esta manera podemos proteger y asegurar datos utilizando AES.
En este post vamos a ver cómo cifrar y descifrar datos con AES en MySQL.
Sintaxis de aes_encrypt y aes_decrypt
Ambas funciones trabajan de una manera similar. Reciben como primer argumento el texto a cifrar o descifrar, y como segundo argumento la clave.
Es decir:
aes_encrypt("texto_plano", "clave")
Y:
aes_decrypt("texto_cifrado", "clave")
Es importante mencionar que al encriptar se devuelven datos binarios, así que hay que definir (dentro de la tabla) el tipo de dato como BLOB, o utilizar una alternativa que veremos más abajo.
Ejemplo de cifrado y descifrado AES con MySQL
Primero, una consideración sobre la contraseña: nunca guardes la contraseña o clave de cifrado en la base de datos, guárdala en un lugar separado o ni siquiera la guardes.
Es decir, si la encriptación de datos es distinta por usuario haz que la clave sea algo como un hash con su contraseña, o cosas de esas. Si la encriptación es general, usa la misma clave para todos, pero mantenla en un lugar seguro.
Voy a crear esta tabla sin fijarme en relaciones, buenas prácticas ni normalización (si no sabes cómo trabajar con MySQL mira este tutorial):
create table tarjetas_credito(
id bigint unsigned auto_increment,
numero text,
ccv blob,
primary key(id)
);
Estamos definiendo una tabla que guardará tarjetas de crédito.
En este caso el número estará en texto plano pero el CCV estará en BLOB, es decir, en modo binario para que soporte los valores encriptados. Si quieres cifrar más datos, agrégalos y define el tipo como BLOB.
Al insertar un registro vamos a llamar a aes_encrypt
:
insert into tarjetas_credito
(numero, ccv)
values
("0123456789", aes_encrypt("123", "hunter2"));
En texto plano, el CCV es 123. Lo ciframos con la clave hunter2.
Si listamos el contenido de la tabla (o si un atacante la roba) veremos lo siguiente:
Son datos raros, incluso la salida podría mostrarse extraña y con espacios extraños; esto es porque el texto ya está cifrado.
Para descifrar se invoca a aes_decrypt
pasándole la misma clave con la que hicimos el cifrado, como primer argumento se indica el valor cifrado.
select numero, aes_decrypt(ccv, "hunter2") from tarjetas_credito;
Si indicamos una contraseña errónea, la función devuelve null la mayoría de veces, aunque podría devolver “basura” en algunas ocasiones, es decir, nunca confíes en que siempre devolverá null para claves erróneas:
En caso de indicar la clave correcta el dato se muestra en texto plano, es decir, descifrado:
Así es como podemos cifrar y descifrar con AES en MySQL. Como lo dije, hay que mantener la clave en otro lugar.
No te confundas, para probar insertamos el dato 123 como CCV y lo ciframos con la clave hunter2.
Fue un ejemplo básico pero podemos adaptarlo a nuestras necesidades o agregarle más características (podríamos definir un alias a la columna) interactuando con la base de datos desde un lenguaje de programación.
Sobra mencionar que podemos hacer un update
y todo tipo de consultas o modificaciones que hacemos normalmente.
aes_encrypt y aes_decrypt sin BLOB
Como lo dije, hay una alternativa a utilizar BLOB y es convertir los datos binarios a su representación hexadecimal al guardarlos; y hacer el proceso inverso al seleccionarlos.
La función que convierte un dato a hexadecimal es HEX
, y la función que hace lo inverso es UNHEX
.
Ahora se puede definir la tabla para que guarde un valor de tipo TEXT y no BLOB:
create table tarjetas_credito(
id bigint unsigned auto_increment,
numero text,
ccv text,
primary key(id)
);
Al hacer la inserción ciframos con aes_encrypt
pero el resultado lo pasamos a través de HEX
y el resultado de ésta última invocación es el que se guarda:
insert into tarjetas_credito
(numero, ccv)
values
("0123456789", hex(aes_encrypt("123", "hunter2")));
Si recuperamos los datos ahora ya estarán como texto, pues tendrán su forma hexadecimal.
Para descifrar los datos invocamos a aes_decrypt
pero los datos cifrados serán el resultado de invocar a UNHEX
:
select numero, aes_decrypt(unhex(ccv), "hunter2") from tarjetas_credito;
Eso sí, los datos hexadecimales ocupan más espacio, pero son legibles y portables. Además, no necesitamos declarar el tipo de dato BLOB.
Para terminar, dejo un enlace al manual de MySQL.
Excelente artículo