En nosotros la seguridad no es una capa añadida a posteriori, es el centro de la arquitectura. El aislamiento de datos, la separación de identidades y la escritura idempotente están grabados en la capa más profunda del código: no son un ajuste que se pueda desactivar, sino la forma en que funciona el sistema.
Aislamiento a nivel de fila
Escritura idempotente
Consulta parametrizada
Aislamiento por loopback
Aislamiento de datos
Aislamiento a nivel de fila: fail-closed
En cada tabla con alcance de inquilino, PostgreSQL Row-Level Security FORCE está activado; la política vincula cada fila a la condición "tenant_id = current_setting('app.current_tenant')".
Una consulta que llega sin establecer el context del inquilino devuelve cero filas, ni siquiera un error, silenciosamente vacía. Ver los datos de una clínica con la sesión de otra clínica es arquitectónicamente imposible.
El test de aislamiento entre inquilinos está obligatoriamente en verde en la integración continua (CI); si está en rojo, el código no se fusiona.
Escritura atómica
Identidad local a la transacción, operación atómica
La identidad del inquilino se establece AL PRINCIPIO de la transacción en cada operación de escritura y solo es válida durante esa transacción; no se adhiere al pool de conexiones ni se filtra a otras solicitudes.
Las operaciones multientidad (factura + línea + pago + descuento de stock) son atómicas dentro de una única transacción: o se escriben todas juntas o ninguna. No se genera un registro incompleto o inconsistente.
Autorización
La autorización por roles (RBAC) se impone en el servidor
El control de autorización se realiza en la capa de la API; nunca se confía en el cliente ni en el frontend. Ocultar un menú en el navegador es solo cuestión de apariencia: la decisión real está en el servidor.
Las mutaciones del núcleo (Fase 1) están protegidas según los roles de admin, recepcionista y médico. Aunque un rol no autorizado invoque el endpoint, la solicitud se rechaza.
Protección contra repeticiones
Escritura idempotente: sin operaciones duplicadas
Cada solicitud de escritura lleva una Idempotency-Key; el bloqueo del lado del servidor impide la segunda operación que llegue con la misma clave.
Aunque la red se caiga o el cliente reenvíe la misma solicitud, no se genera un segundo cobro ni una segunda factura. El resultado es siempre único y predecible.
Seguridad de consultas
Inyección SQL imposible: consulta parametrizada
Todas las consultas a la base de datos son sentencias totalmente parametrizadas, generadas en tiempo de compilación con sqlc. En la base de código no hay una sola línea que construya SQL concatenando cadenas a mano con entrada del usuario.
Esto elimina estructuralmente toda la clase de ataques de inyección SQL: no se basa en un esfuerzo de "filtrado" o "escape", sino en la forma en que se construye la consulta.
Superficie de red
Aislamiento por loopback: el núcleo cerrado al exterior
El API central solo escucha en 127.0.0.1 (loopback). Cualquier intento de vincularse a 0.0.0.0 o a una IP pública se rechaza al inicio; el núcleo nunca se expone a internet bajo ninguna circunstancia.
La única superficie pública es una puerta ligera (BFF): no contiene lógica de negocio más allá de la sesión, el renderizado y CSRF. El navegador nunca puede ver ni direccionar el API central directamente.
Identidad y sesión
Contraseña con argon2id, clave de sesión opaca
Las contraseñas se cifran con argon2id, el estándar del sector resistente en memoria; no se utilizan hashes planos ni débiles.
Las claves de sesión son valores opacos criptográficamente aleatorios de 32 bytes, se almacenan en Redis y expiran por TTL. Al navegador solo se refleja una cookie HttpOnly + Secure + SameSite; la clave nunca se expone a JavaScript.
Auditoría
Registro de auditoría inalterable (append-only)
Las operaciones importantes se escriben en el registro de auditoría append-only dentro de la MISMA transacción que la mutación que las desencadena. Si la operación se revierte, tampoco se escribe el registro de auditoría: los dos son inseparables.
A la tabla de auditoría se le retiró el permiso de UPDATE/DELETE; los registros no pueden editarse ni eliminarse después. El pasado permanece tal cual.
Privacidad
Enmascarado de datos sensibles
El TCKN, la contraseña, la clave de sesión y los datos de salud no entran en los registros de ninguna forma. Estos campos se enmascaran incluso en las salidas de depuración.
Cuando es necesario, los campos sensibles se cifran a nivel de columna. Los datos de salud de categoría especial se protegen con reglas incorporadas al diseño desde el principio.
Integridad monetaria
Seguridad monetaria: numeric, contraasiento
El dinero se guarda en todas partes como numeric(14,2); los cálculos monetarios no se hacen con float. El redondeo ocurre en un único punto, en el servidor, de modo que no se produce desfase de céntimos ni diferencia de redondeo.
La cancelación y la devolución se procesan como un contraasiento en lugar de borrar el registro. El saldo de la cuenta no se escribe a mano; siempre se deriva de la suma de facturas y pagos. El pasado no se altera.
Transferencia
Seguridad de transporte y navegador
Todo el tráfico se transporta sobre HTTPS/TLS y HSTS está activado; el navegador establece la conexión únicamente cifrada.
Una política estricta de seguridad de contenido (CSP) bloquea los scripts inline; X-Frame-Options DENY previene el clickjacking, X-Content-Type-Options nosniff evita la confusión de tipos MIME. También se aplican Referrer-Policy y Permissions-Policy.
Importante
Separación de niveles de identidad + plano de administración separado
El usuario de clínica, el portal del paciente y el operador de plataforma son tiers de identidad SEPARADOS: encabezado propio, espacio de sesión propio. La clave de un tier no funciona en otro: los límites de autorización están rigurosamente separados.
Los endpoints de plataforma y administración (aprovisionamiento de inquilinos, facturación, gestión de automatización/complementos, generación de claves API) NO ESTÁN presentes en el API público.
Estas funciones operan en un plano de control separado, interno y con un patrón de identidad distinto (clave de administración propia); no es accesible ni descubrible desde fuera. Así, la superficie de ataque se reduce notablemente.
CÓMO FUNCIONA
Una sola transacción, aislamiento fail-closed
El siguiente flujo conceptual muestra cómo cada escritura queda bloqueada al inquilino y permanece idempotente. Es solo con fines ilustrativos.
# 1) El contexto de inquilino se establece AL PRINCIPIO de la transacción (exclusivo de esa tx)
BEGIN;
SELECT set_config('app.current_tenant', :session_tenant_id, true);
# 2) Bloqueo con Idempotency-Key: con la misma clave no se crea una segunda operaciónSELECT lock_idempotency(:idempotency_key);
# 3) Escritura multi-entidad: todo atómico en una sola txINSERT INTO invoices ...; # RLS: tenant_id cubierto automáticamenteINSERT INTO payments ...;
INSERT INTO audit_log ...; # append-only, la misma tx
COMMIT;
# Si no se establece el context, la política RLS no devuelve ninguna fila:SELECT * FROM patients; --> 0 fila (no un error, silenciosamente vacía)
Verificable de forma independiente
Parte de la seguridad de transporte es observable desde fuera: puede inspeccionar usted mismo el certificado HTTPS/TLS de su conexión, así como cabeceras de seguridad de respuesta como HSTS y la política de seguridad de contenido, desde las herramientas de desarrollo de su navegador. Nuestras afirmaciones se basan en controles arquitectónicos, no en frases de marketing.
¿Tiene alguna pregunta sobre la arquitectura de seguridad?
Nuestro equipo estará encantado de hablar con usted sobre los detalles técnicos y sus requisitos de cumplimiento.