Feature: cifrado en reposo de los datos confidenciales
Qué
Los datos confidenciales se guardan cifrados con AES-256-GCM:
- Estudiante: información de salud, sistema de salud y motivo de derivación.
- Comentarios confidenciales de estudiante y de instancia (contenido).
Se cifran al guardar y se descifran al leer con un AttributeConverter de JPA, así que la API y el front no cambian.
Por qué
Pendiente del checklist: cifrado en reposo de datos confidenciales. No se usa el mismo mecanismo que la contraseña porque eso es un hash de una vía (BCrypt) y estos datos se tienen que poder volver a leer.
Cambios
-
CifradorDatosConfidenciales: AES-256-GCM con IV aleatorio. Guardaenc:v1:+ Base64(IV + texto cifrado + tag). Detecta valores alterados o una clave equivocada. -
DatoConfidencialConverter: se aplica con@Converten los campos deEstudiantey encontenidodeComentarioConfidencialEstudiante/ComentarioConfidencialInstancia(no enComentarioBase, que comparten los comentarios normales). -
MigracionCifradoRunner: al iniciar, cifra lo que haya quedado en claro. Es idempotente, usa una transacción por tabla y un savepoint por fila. Apaga el trigger deupdated_atsolo dentro de la transacción para no marcar los comentarios como "editado". Se puede desactivar conCIFRADO_MIGRAR_AL_INICIAR=false. - Mientras tanto, los valores viejos en claro se siguen leyendo.
-
sis_saludpasa aTEXTsin el CHECK de formato (el valor cifrado es más largo). La validación de máx. 80 caracteres y del formato sigue en los DTOs. - Nueva variable
DATA_ENCRYPTION_KEY(documentada en.env.example). Los tests usan una clave fija y no corren la migración.
Cambios en BD
Correr en cada base existente ANTES de desplegar:
ALTER TABLE proyecto.estudiantes DROP CONSTRAINT IF EXISTS chk_sis_salud_valido;
ALTER TABLE proyecto.estudiantes ALTER COLUMN sis_salud TYPE TEXT;
proyecto_schema.sql ya trae el cambio para las bases nuevas.
Variables de entorno
-
DATA_ENCRYPTION_KEY(obligatoria):openssl rand -base64 32. Cada ambiente tiene la suya. No se puede perder ni cambiar una vez que hay datos cifrados. - Para correr los tests de integración, exportar la misma clave que usa la app contra esa base.
Cómo probar
- Correr el script SQL y agregar
DATA_ENCRYPTION_KEYal.env. - Levantar la app: en el log aparece
[AUDIT] Cifrado en reposo: N filas de ... cifradas. - En la BD,
inf_esta_salud,sis_salud,motivo,obs_confidencialycomentario(confidenciales) empiezan conenc:v1:. - En la app, la ficha de salud y los comentarios confidenciales se ven normal y los que no se habían editado no aparecen como "editado".
- Crear o editar un comentario confidencial: queda cifrado en la BD.
- Reiniciar: la migración no vuelve a tocar nada.
Notas
- La auditoría registra un UPDATE por fila migrada, sin el contenido (el trigger ya omitía estas columnas).
- Una fila que no cumple algún CHECK queda en claro y se registra con su id. Hoy pasa con estudiantes que superan los 100 años por
chk_edad_rango(bug aparte). - Los informes médicos (archivos en disco) quedan fuera de este MR.