Seguridad¶
Todas las plataformas del ecosistema se levantan sobre una misma línea base de seguridad. Esta página la define: al añadir una plataforma nueva, trate estos puntos como requisitos, no como opciones.
Defensa en profundidad¶
Las capas se apilan bajo el principio de que ninguna basta por sí sola:
| Capa | Protección |
|---|---|
| Edge (nginx) | TLS, HSTS, rate limit, límite de tamaño del cuerpo |
| Aplicación (HTTP) | Cabeceras de seguridad, allow-list de CORS, CSRF, timeouts |
| Aplicación (lógica) | RBAC, comprobación de permisos, validación de entrada |
| Base de datos | RLS, consultas parametrizadas |
| Auditoría | Registro de auditoría encadenado por hash |
El token nunca llega al JS del cliente¶
El patrón BFF (Backend-for-Frontend) es la base de la seguridad del frontend.
El navegador nunca habla directamente con el backend: solo habla con rutas de Next.js del mismo dominio, y estas hacen de proxy hacia el backend desde el servidor. El token vive en una cookie httpOnly.
El resultado: el XSS deja de ser un vector de robo de tokens. Aunque un atacante logre ejecutar JavaScript, no alcanza el token.
Encima se añade doble protección CSRF: cabecera propia más comprobación de origin.
Cabeceras de seguridad¶
En todas las respuestas:
| Cabecera | Propósito |
|---|---|
Content-Security-Policy |
Cierra el vector de ejecución del XSS |
Strict-Transport-Security |
Fuerza HTTPS (HSTS) |
Cross-Origin-Opener-Policy |
Aísla frente a ataques entre ventanas |
Cross-Origin-Embedder-Policy |
Controla los recursos externos |
Cross-Origin-Resource-Policy |
Limita el uso externo de los recursos |
El CORS funciona por allow-list — nunca *.
Row-Level Security de Postgres¶
El aislamiento de los datos de organizaciones y usuarios se aplica a nivel de
base de datos, no con una cláusula WHERE en el código de la aplicación.
Por qué: basta un WHERE user_id = ? olvidado en el código para que se filtren
datos. La RLS aplica esa comprobación a todas las consultas, al margen de
los errores de programación.
Guard de aplicabilidad en el arranque
Al arrancar, la aplicación comprueba que la RLS esté realmente activa. Si falta una policy, o si el usuario de conexión tiene derecho a saltarse la RLS, la aplicación se niega a arrancar.
El motivo es que una RLS desactivada en silencio es el peor caso posible: todo aparenta funcionar con normalidad.
SQL¶
- Consultas parametrizadas — sin concatenación de cadenas.
- Sin ORM — SQL escrito a mano sobre
pgx. Cada consulta queda a la vista.
Rate limits y timeouts¶
| Protección | Dónde |
|---|---|
| Rate limit de autenticación (estricto) | Rutas /login, /oauth, /auth, /api/auth |
| Rate limit general de la app (laxo) | El resto de rutas |
| Rate limit de API | API públicas del tipo /v1/* |
| Timeouts del servidor HTTP | read · write · idle · header — todos configurados |
| Límite de tamaño del cuerpo | En el edge |
Límites estrictos solo en la autenticación
Poner el rate limit estricto de autenticación en todas las rutas hace
que los prefetch RSC de Next.js superen el límite y reciban 503. Por eso el
límite estricto se aplica solo a las rutas de autenticación, y uno laxo al
resto.
Gestión de secretos¶
- Cifrado de tokens — los tokens OAuth de terceros (Google Drive, Dropbox, etc.) se guardan cifrados con AES-256-GCM.
- Claves criptográficas — en HSM; el HSM on-premise y el de la nube se respaldan mutuamente.
- Secretos de la aplicación — por variables de entorno, nunca en git.
No ponga secretos en el repositorio
Credenciales de servidor, claves de API, client secret, PIN del HSM: nada de esto se escribe jamás en un repositorio, un script o la documentación. Los scripts de despliegue reciben los secretos solo por variables de entorno.
Si un secreto queda expuesto por accidente: (1) rótelo de inmediato, (2) revise los registros de acceso, (3) muévalo a un sistema de gestión de secretos.
Endpoints cerrados en producción¶
/metrics y /swagger exponen la estructura interna y el listado de endpoints.
En producción están protegidos con bearer token.
Registro de auditoría¶
Un registro encadenado por hash y solo de adición. Cada entrada contiene el hash de la anterior.
Lo que eso significa: ninguna entrada intermedia puede borrarse ni alterarse — la cadena se rompe y la comprobación de integridad falla. Solo lo leen los administradores, y existe una operación aparte para verificar la integridad.
Firma y no repudio¶
Los actos con consecuencias jurídicas exigen firma con PIN2: la autenticación (PIN1) no basta. Véase Autenticación y autorización para el detalle.
La clave privada de firma está dividida mediante ECDSA de umbral 2-of-2: ninguna de las partes puede firmar por sí sola.
Pruebas¶
- Pruebas unitarias — lógica de negocio y comprobaciones de permisos.
- Pruebas de integración con testcontainers — contra PostgreSQL/Redis reales, que ejercitan la RLS de verdad.
- Pruebas de golden vector — compatibilidad criptográfica a nivel de protocolo.
Lista de comprobación de seguridad¶
Antes de que una plataforma nueva salga a producción:
- [ ] TLS + HSTS activos, certificados que se renuevan automáticamente
- [ ] Todas las cabeceras de seguridad configuradas (CSP · HSTS · COOP · COEP · CORP)
- [ ] Allow-list de CORS — sin
* - [ ] Rate limits: estricto en autenticación, laxo en lo demás
- [ ] Todos los timeouts del servidor HTTP configurados
- [ ] RLS activa y guard de arranque funcionando
- [ ] Todas las consultas parametrizadas
- [ ] Tokens en cookies httpOnly, sin llegar al JS del cliente
- [ ] Doble protección CSRF
- [ ]
/metricsy/swaggercerrados - [ ] Registro de auditoría escribiéndose, integridad verificable
- [ ] Secretos por variables de entorno, ausentes del repositorio
- [ ] Pruebas de integración que ejercitan la RLS de verdad