Wiki / Documentación interna /
Arquitectura
Seguridad, cifrado y RLS
Datos personales de profesionales sanitarios: la especialidad médica y la pertenencia a una sociedad científica se tratan como dato profesional sensible.
Cifrado de campos sensibles
-
src/lib/crypto/campos.ts: AES-256-GCM vía Web Crypto, formatov1:<iv_base64>:<ciphertext+tag_base64>. -
Clave en
ENCRYPTION_KEY(32 bytes base64); rotación soportada conENCRYPTION_KEY_PREVIOUS(descifra probando ambas, cifra siempre con la principal). No existe todavía un script de rotación automatizado. -
descifrar()nunca lanza: si falla con las dos claves devuelvenull, para que un valor ilegible no tumbe la ficha entera del socio. -
enmascararIban()muestra país + 4 últimos dígitos; revelarlo completo exige permiso y queda auditado. - NIF en claro, a propósito (ADR 003): cifrarlo rompía el índice único, la búsqueda por trigram y la deduplicación en importación. Protegido solo por RLS + cifrado en reposo de Supabase + exclusión de exportaciones.
-
Lo que este cifrado NO protege: al operador de la
plataforma. Quien controla Vercel/Supabase controla
ENCRYPTION_KEY. Protege frente a un volcado de base de datos, un backup extraviado o una consulta indebida desde el panel de Supabase por alguien sin esa clave — no frente a quien la tiene.
Auditoría (append-only)
-
private.audit_log, particionada por mes (partition by range (occurred_at)), particiones futuras creadas automáticamente 12 meses por delante. -
Escritura solo vía trigger genérico
private.registrar_auditoria()— nunca desde código de aplicación. -
RLS activada, sin ninguna política de acceso directo +
REVOKE ALL FROM public: doble cerrojo, ni con política ausente ni con permisos de tabla se puede escribir o leer directamente. -
Los valores de campos cifrados nunca aparecen: se sustituyen por
"[modificado]"(private.campos_no_registrables()). -
Lectura vía
public.consultar_auditoria(...)(security definer): superadmin ve todo,society_adminsolo su sociedad. Visor en/admin/auditoria, con exportación a Excel. - Retención mínima 24 meses declarada; sin job de purga — coherente con "mínimo", no un máximo automático.
RGPD / derechos ARCO-POL
No construido todavía. No existe función real de exportar los
datos de un socio en JSON+PDF, anonimizar, ni borrado con periodo de
gracia de 30 días. Tampoco existe
docs/RGPD.md como
archivo — docs/SEGURIDAD.md lo declara pendiente
explícitamente, para no aparentar un cumplimiento que aún no es
real.
Lo más cercano que existe hoy es la copia de seguridad por sociedad (ver CI/CD, Vercel y Supabase), pero está pensada como backup del superadmin, no como ejercicio de un derecho ARCO-POL del socio: no descifra ni excluye campos.
Autenticación segura
-
Comprobación HaveIBeenPwned (k-anonymity) en
src/lib/seguridad/hibp.ts. -
Rate limiting con bloqueo progresivo, resuelto en Postgres
(
private.rate_limits), buckets:login,recuperar,alta_publica,sociedad_por_email,crear_ticket. Diseño fail-open: si el RPC de rate limit falla, se permite el intento — un fallo de infraestructura no debe apagar el login de todo el mundo. - Detalle de 2FA, modo soporte y roles en Roles, RLS y superadmin.