La info que guardas en la base de datos suele tener un valor enorme para la empresa. Pero, por desgracia, también es muy atractiva para los malos. Por eso es importante pensar en el cifrado: es una de las formas de proteger los datos de miradas ajenas.
El cifrado ayuda a proteger info confidencial: por ejemplo, contraseñas, números de tarjetas de crédito o datos personales. También ayuda a cumplir con leyes como GDPR o HIPAA. Y si de repente hay una fuga, los datos cifrados serán mucho menos vulnerables, lo que reduce el daño potencial.
En PostgreSQL tienes funciones muy cómodas para cifrado simétrico. Con pgp_sym_encrypt(data, key) puedes cifrar los datos que quieras, y luego descifrarlos con pgp_sym_decrypt(encrypted_data, key) usando la misma clave. Es fácil — y seguro.
Ejemplo de cifrado de datos
Paso 1: Crear la tabla
Vamos a crear una tabla users con una columna que va a guardar los números de teléfono cifrados:
-- Crear tabla con columna para guardar datos cifrados
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username TEXT NOT NULL,
phone_encrypted BYTEA -- Aquí se guardarán los números de teléfono cifrados
);
Paso 2: Añadir datos con cifrado
Ahora vamos a añadir un usuario, cifrando su número de teléfono:
-- Insertar datos con cifrado
INSERT INTO users (username, phone_encrypted)
VALUES ('john_doe', pgp_sym_encrypt('123-456-7890', 'my_secret_key'));
Fíjate cómo usamos la función pgp_sym_encrypt. my_secret_key es nuestra clave simétrica. En la vida real, la clave debería ser compleja y estar bien protegida.
Paso 3: Extraer datos con descifrado
Cuando toque acceder a los datos, podemos descifrarlos así:
-- Extraer datos con descifrado
SELECT
username,
pgp_sym_decrypt(phone_encrypted, 'my_secret_key') AS phone
FROM users;
Si la clave es correcta, verás el número de teléfono original.
Subiendo el nivel: añadir cifrado a una tabla existente
¿Y si la tabla ya existe y queremos empezar a cifrar datos en una de sus columnas? Mira este ejemplo.
Paso 1: Crear una nueva columna
Supón que tenemos la tabla customers y queremos cifrar las columnas con los números de tarjeta de crédito:
-- Añadir nueva columna para datos cifrados
ALTER TABLE customers ADD COLUMN card_number_encrypted BYTEA;
Paso 2: Pasar los datos a la columna cifrada
Ciframos los datos existentes y los pasamos a la nueva columna:
-- Cifrar datos y pasarlos a la nueva columna
UPDATE customers
SET card_number_encrypted = pgp_sym_encrypt(card_number, 'my_other_secret_key');
Paso 3: Eliminar la columna sin cifrar
Después de cifrar los datos, puedes borrar la columna antigua:
-- Eliminar la columna antigua sin cifrar
ALTER TABLE customers DROP COLUMN card_number;
Ahora los datos están protegidos por cifrado y solo puedes acceder a ellos si tienes la clave.
Peculiaridades al trabajar con datos cifrados
Hay varias cosas importantes cuando trabajas con columnas cifradas:
Tipo de datos:
- Los valores cifrados se guardan en formato binario (
BYTEA), no en texto legible. - En las consultas tienes que usar funciones de descifrado.
Búsqueda y filtrado:
- No puedes buscar directamente filas por datos cifrados, por ejemplo:
SELECT * FROM users WHERE phone_encrypted = '123-456-7890'; -- ¡NO FUNCIONA!
- En vez de eso, puedes descifrar los datos en la consulta:
SELECT *
FROM users
WHERE pgp_sym_decrypt(phone_encrypted, 'my_secret_key') = '123-456-7890';
Rendimiento:
Cifrar y descifrar datos puede hacer que las consultas vayan más lentas. Úsalo solo donde realmente lo necesites.
Escenario real: proteger contraseñas
Guardar contraseñas es una de las tareas más típicas de cifrado. En vez de guardar contraseñas en texto plano (muy mala idea), hay que hacerles hashing.
Hashing de contraseña usando pgcrypto
Vamos a usar la función crypt() para hacer hashing seguro de contraseñas:
-- Hashing de contraseña al insertar registro
INSERT INTO users (username, phone_encrypted)
VALUES ('alice', crypt('mi_contraseña_segura', gen_salt('bf')));
Aquí gen_salt('bf') crea la sal para el hashing de la contraseña.
Para comprobar la contraseña, comparamos su hash:
-- Comparar contraseña hasheada
SELECT username
FROM users
WHERE crypt('mi_contraseña_segura', phone_encrypted) = phone_encrypted;
Consejos de seguridad
- Guarda las claves aparte:
Nunca guardes las claves simétricas en la misma base de datos que los datos cifrados.
- Usa claves complejas:
Claves simples como "123" se pueden adivinar muy fácil.
- Actualiza las claves regularmente:
Para evitar fugas de datos, es recomendable cambiar las claves de vez en cuando y volver a cifrar los datos.
GO TO FULL VERSION