1. Visión General — Modelo de 4 Capas
Nvito implementa un modelo de seguridad en profundidad (defense-in-depth) con 4 capas independientes. Cada capa actúa como un filtro: si una falla, las siguientes continúan protegiendo el sistema.
Modelo de 4 Capas — Defense in Depth
Capa 1: Cloudflare Edge
Capa 2: Worker Proxy (solo PROD)
Capa 3: Zero Trust Access
Capa 4: Auth Aplicativa
Capa 4: Auth Aplicativa
| Capa | Qué protege | Aplica a |
|---|---|---|
| 1. Cloudflare Edge | Tráfico de red (DDoS, bots, exploits conocidos) | Todos los subdominios |
| 2. Worker Proxy | IP del origin, autenticación API-a-API | Solo api.nvito.mx en producción |
| 3. Zero Trust Access | Acceso no autorizado al panel y docs | Solo admin.nvito.mx y docs.nvito.mx |
| 4. Auth Aplicativa | Acceso a datos, RBAC, token theft, CSRF, XSS | Cada aplicación con su mecanismo |
2. Capa 1: Cloudflare Edge
Todo el tráfico hacia nvito.mx y sus subdominios pasa por la red edge de Cloudflare antes de llegar a cualquier origin server. Esta capa es transparente para la aplicación y opera a nivel de red/transporte.
2.1 DDoS Mitigation
Cloudflare provee mitigación automática de DDoS en 3 niveles:
| Nivel | Tipo de ataque | Mitigación |
|---|---|---|
| L3/L4 | SYN flood, UDP flood, amplification | Filtrado automático en el edge, sin configuración |
| L7 | HTTP flood, slowloris, request bursts | Análisis de patrones + challenge automático |
| DNS | DNS amplification, query flood | Anycast absorbe el volumen |
La mitigación L3/L4 está siempre activa en todos los planes de Cloudflare (incluido Free). La mitigación L7 se beneficia del plan Pro para reglas WAF gestionadas adicionales.
2.2 WAF (Web Application Firewall)
| Funcionalidad | Plan Free | Plan Pro |
|---|---|---|
| Cloudflare Managed Ruleset | Si (reglas criticas) | Si (completo: SQLi, XSS, RCE, etc.) |
| OWASP Core Ruleset | Parcial (solo reglas criticas) | Completo |
| Custom Rules | 5 reglas | 20+ reglas |
| Rate Limiting Rules | 1 regla | 5+ reglas |
| Security Level | Configurable | Configurable |
Nvito actualmente opera en el plan Free con el Cloudflare Managed Ruleset desplegado. Este ruleset incluye reglas criticas de protección pero el OWASP Core Ruleset completo requiere el plan Pro ($20/mes). La protección actual se complementa con:
- Custom rules para bloquear User-Agents sospechosos
- Rate limiting configurado: 100 req/min por IP para DEV/TEST
- Bot Fight Mode habilitado
- Cache Rules: bypass de cache para subdominios
dev-*ytest-*
Plan Pro para producción
Se recomienda activar el plan Pro de Cloudflare ($20/mes) al lanzar a producción para obtener las managed rules WAF. Mientras tanto, la aplicación implementa sus propias validaciones contra SQLi y XSS a nivel de código (Zod validation, DOMPurify, CSP).
2.3 SSL/TLS
| Configuración | Valor |
|---|---|
| Modo | Full (Strict) |
| TLS mínimo | 1.2 |
| HSTS | max-age=31536000; includeSubDomains |
| HTTPS Redirect | Automático (Always Use HTTPS) |
| Certificate Transparency | Habilitado |
El modo Full (Strict) significa que Cloudflare verifica que el certificado del origin sea válido y no este expirado. Esto previene ataques man-in-the-middle entre Cloudflare y el origin.
2.4 Rate Limiting (3 capas)
Nvito implementa rate limiting en 3 capas independientes que se complementan:
Rate Limiting — 3 Capas
| Capa | Regla | Aplica a | Threshold | Acción |
|---|---|---|---|---|
| Cloudflare | API Rate Limit | api.nvito.mx/* | 100 req/min por IP | Block (429) |
| nvito-api | UserThrottlerGuard | Todos los endpoints autenticados | Configurable por endpoint | 429 por usuario |
| nvito-invitations | Rate limiter in-memory | /api/revalidate | 10 req/IP/60s | 429 con Retry-After |
2.5 Bot Fight Mode
Bot Fight Mode está habilitado en Cloudflare. Analiza patrones de comportamiento (fingerprinting JS, headless browser detection, TLS fingerprint) y desafía automáticamente a bots sospechosos sin afectar a usuarios legítimos.
Bot Fight Mode
3. Capa 2: Cloudflare Worker (API Proxy)
En producción, api.nvito.mx no apunta directamente a Railway. En su lugar, un Cloudflare Worker intercepta todas las requests y las redirige al origin después de inyectar un token de autenticación API-a-API.
3.1 Arquitectura del Worker
Cloudflare Worker — API Proxy
3.2 Beneficios del Worker Proxy
| Beneficio | Detalle |
|---|---|
| IP del origin oculta | La URL pública de Railway (*.up.railway.app) nunca se expone. El tráfico solo llega vía Cloudflare. |
| Autenticación API-a-API | El header X-CF-API-Token garantiza que solo requests que pasen por Cloudflare llegan al backend. Requests directas a Railway son rechazadas con 403. |
| Edge computing | El Worker corre en 300+ data centers de Cloudflare. La latencia de inyección del header es de menos de 1ms. |
| Rate limiting doble | Cloudflare rate-limita en el edge; el backend puede aplicar su propio rate limiting adicional. |
3.3 Límites del Free Tier
| Recurso | Límite Free |
|---|---|
| Requests/dia | 100,000 |
| CPU time/request | 10ms |
| Script size | 1 MB |
| Subrequests/request | 50 |
Con el tráfico esperado en las primeras fases de Nvito, el free tier es más que suficiente. El Worker es extremadamente ligero (solo inyecta un header y hace un fetch al origin).
3.3 CloudflareTokenGuard (nvito-api)
En el lado del backend, nvito-api implementa un guard global llamado CloudflareTokenGuard que valida el header X-CF-API-Token inyectado por el Worker.
| Aspecto | Detalle |
|---|---|
| Archivo | src/common/guards/cloudflare-token.guard.ts |
| Posición | Primer guard global (antes de ClerkAuthGuard) |
| Comparación | Timing-safe (crypto.timingSafeEqual) para prevenir timing attacks |
| Bypass | En desarrollo/test, si CF_API_TOKEN no está configurado, el guard permite el paso |
| Tests | 9 tests unitarios cubriendo token válido, inválido, ausente y bypass |
Cadena de guards actualizada para cada request:
Cloudflare Access — Doble Autenticacion
3.4 Configuración del Worker
| Variable | Valor | Tipo |
|---|---|---|
CF_API_TOKEN | Token secreto compartido | Secret (Cloudflare Worker + Railway) |
RAILWAY_API_URL | https://nvito-api-production.up.railway.app | Variable de entorno |
El Worker es extremadamente ligero: solo inyecta el header y hace un fetch al origin. Latencia agregada inferior a 1ms.
Solo en producción
El Worker proxy solo se usa en producción (api.nvito.mx). En DEV y TEST, los subdominios dev-api.nvito.mx y test-api.nvito.mx apuntan directamente al VPS via registro A. El CloudflareTokenGuard detecta automáticamente la ausencia de CF_API_TOKEN y permite el paso en estos ambientes.
4. Capa 3: Cloudflare Access (Zero Trust)
Cloudflare Access protege subdominios seleccionados con autenticación Zero Trust antes de que el request llegue al origin. Actúa como un reverse proxy de identidad.
4.1 Subdominios Protegidos
| Subdominio | Protegido por CF Access | Razón |
|---|---|---|
admin.nvito.mx | Si | Panel administrativo con datos sensibles |
dev-admin.nvito.mx | Si | Mismo contenido que producción, datos de desarrollo |
test-admin.nvito.mx | Si | Mismo contenido que producción, datos de QA |
docs.nvito.mx | Si | Documentación técnica confidencial |
api.nvito.mx | No | API pública consumida por clientes programáticos |
app.nvito.mx | No | PWA pública para invitados (auth propia via BFF) |
inv.nvito.mx | No | Invitaciones públicas para invitados finales |
4.2 Flujo de Autenticación
Cadena de Guards — Admin
4.3 Configuración de Access
| Parámetro | Valor |
|---|---|
| Organización Zero Trust | Creada en Cloudflare |
| Identity Provider | One-time PIN (email OTP) |
| Método de autenticación | Email OTP (One-Time Pin) |
| Emails permitidos | Lista explícita de emails autorizados |
| Duración de sesión | 24 horas |
| Reautenticación | Cada 24 horas o al cerrar el browser |
| Usuarios incluidos en Free | Hasta 50 usuarios |
Doble autenticación en admin
Para acceder a admin.nvito.mx, un usuario debe pasar dos capas de autenticación independientes: primero Cloudflare Access (email OTP) y luego Clerk (email + password). Esto es intencional: CF Access protege a nivel de red, Clerk protege a nivel de aplicación con RBAC granular.
4.4 Aplicaciones Access
Se han configurado 2 aplicaciones en Cloudflare Access, una para cada ambiente no productivo:
| Aplicación | Subdominios protegidos |
|---|---|
| Nvito DEV Environment | dev-api.nvito.mx, dev-admin.nvito.mx, dev-app.nvito.mx, dev-inv.nvito.mx, dev-landing.nvito.mx |
| Nvito TEST Environment | test-api.nvito.mx, test-admin.nvito.mx, test-app.nvito.mx, test-inv.nvito.mx, test-landing.nvito.mx |
En producción, solo admin.nvito.mx y docs.nvito.mx están protegidos por CF Access. Los demás subdominios de producción son públicos (la autenticación se maneja en la capa 4).
4.5 Políticas de Access
Las políticas definen quién puede acceder a cada aplicación protegida:
Politicas de Cloudflare Access
5. Capa 4: Autenticación Aplicativa
Cada aplicación de Nvito implementa su propio mecanismo de autenticación adaptado a su caso de uso.
5.1 nvito-admin — Clerk SSO + RBAC
| Aspecto | Detalle |
|---|---|
| Provider | Clerk 6 (Next.js SDK) |
| Método | Email + password, con opción de OTP |
| Token | JWT firmado por Clerk, verificado en backend |
| RBAC | 8 roles, 40+ permisos granulares |
| Guards (backend) | ClerkAuthGuard + RoleGuard + AdminPanelGuard |
| Multi-org | Header X-Organization-Id para Super/Platform Admin |
Cadena de guards para cada request del admin:
Cadena de Guards — General
5.2 nvito-pwa — BFF con Cookies HttpOnly
| Aspecto | Detalle |
|---|---|
| Patrón | Backend-For-Frontend (BFF) |
| Tokens | JWT almacenados en cookies HttpOnly encriptadas (AES-256-GCM) |
| Cookies | __Host-nvito-at (access, 15min), __Host-nvito-rt (refresh, 30d) |
| CSRF | Double-submit: cookie __Host-nvito-csrf + firma HMAC-SHA256 en __Host-nvito-csrf-sig |
| Proxy | Todo pasa por /api/proxy/* → descifra JWT → forward a nvito-api |
| CSP | connect-src 'self' bloquea llamadas directas al API desde JS |
BFF Proxy — nvito-pwa
5.3 nvito-client — JWT Independiente
| Aspecto | Detalle |
|---|---|
| Auth | JWT propio (independiente de Clerk) |
| Storage | expo-secure-store (encriptado en dispositivo) |
| Guard (backend) | MobileAuthGuard (verifica JWT mobile) |
| Login Host | Código de evento + PIN |
| Login Guest | Access token via deep link / QR |
| Auto-refresh | Proactivo antes de expiración |
5.4 nvito-invitations — Sin Autenticación
| Aspecto | Detalle |
|---|---|
| Auth | Ninguna (contenido público) |
| Protección | Validación de slug (regex + longitud), DOMPurify, CSP strict |
| SSRF prevention | validateCdnUrl() contra whitelist de hosts CDN |
| XSS prevention | escapeJsString() + escapeHtmlAttr() context-aware |
| Frame protection | X-Frame-Options: DENY en rutas públicas |
5.5 Tabla Comparativa
| Feature | nvito-admin | nvito-pwa | nvito-client | nvito-invitations |
|---|---|---|---|---|
| Autenticación externa | Clerk SSO | JWT vía BFF | JWT propio | Ninguna |
| Tokens en browser JS | Sí | No | No | No |
| CSRF protection | Clerk built-in | Double-submit HMAC | N/A (nativo) | N/A (read-only) |
| Rate limiting | UserThrottlerGuard | BFF + API | API guard | Cloudflare edge |
| RBAC | 8 roles, 40+ perms | HOST/GUEST | HOST/GUEST | N/A |
| Multi-org | Sí | No | No | No |
| CF Access | Sí | No | No | No |
| CSP headers | Sí | Sí | No | Sí |
6. Headers de Seguridad por Proyecto
Cada proyecto configura sus propios headers de seguridad HTTP, adaptados a su caso de uso.
6.1 Headers Comunes
| Header | Valor | Propósito |
|---|---|---|
Strict-Transport-Security | max-age=31536000; includeSubDomains | Forzar HTTPS durante 1 año |
X-Content-Type-Options | nosniff | Prevenir MIME type sniffing |
Referrer-Policy | strict-origin-when-cross-origin | Limitar info en header Referer |
Permissions-Policy | camera=(), microphone=(), geolocation=() | Deshabilitar APIs sensibles |
6.2 Content-Security-Policy por Proyecto
nvito-admin (next.config.ts):
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.clerk.accounts.dev;
connect-src 'self' https://api.nvito.mx https://*.clerk.accounts.dev;
img-src 'self' https://cdn.nvito.mx https://img.clerk.com data:;
frame-src https://*.clerk.accounts.dev;
nvito-pwa (next.config.ts):
default-src 'self';
script-src 'self' 'unsafe-inline';
connect-src 'self'; /* Solo BFF local, bloquea API directa */
img-src 'self' https://cdn.nvito.mx data:;
nvito-invitations (next.config.mjs):
/* Rutas públicas /i/* */
default-src 'self';
script-src 'self' 'unsafe-inline';
frame-ancestors 'none'; /* No permitir embeds */
/* Rutas preview /preview/* */
frame-ancestors 'self' https://admin.nvito.mx https://dev-admin.nvito.mx;
6.3 X-Frame-Options
| Proyecto | Valor | Razón |
|---|---|---|
| nvito-admin | SAMEORIGIN | Permite iframes internos (preview de invitaciones) |
| nvito-invitations (público) | DENY | Las invitaciones no deben embeberse en sitios externos |
| nvito-invitations (preview) | SAMEORIGIN | Permite preview iframe desde el admin |
| nvito-pwa | DENY | La PWA no debe embeberse |
7. Ataques Mitigados
La siguiente tabla resume los vectores de ataque más relevantes y cómo cada capa los mitiga.
| Vector de Ataque | Capa | Mecanismo de Mitigación |
|---|---|---|
| DDoS volumétrico (L3/L4) | 1 - CF Edge | Anycast absorbe volumen, filtrado automático |
| DDoS aplicativo (L7) | 1 - CF Edge | Rate limiting, JS Challenge, Bot Fight Mode |
| Brute force login | 1 + 3 + 4 | Rate limiting CF + CF Access OTP + Clerk lockout |
| SQL Injection | 4 - App | Prisma ORM (parametrizado), Zod validation |
| XSS reflejado | 1 + 4 | WAF (Pro), CSP headers, React auto-escape, DOMPurify |
| XSS almacenado | 4 - App | DOMPurify en invitaciones, escapeJsString(), CSP |
| CSRF | 4 - App | Clerk built-in (admin), double-submit HMAC (PWA) |
| Token theft (XSS) | 4 - App | Cookies HttpOnly AES-256-GCM (PWA), SecureStore (mobile) |
| SSRF | 4 - App | validateCdnUrl() whitelist en invitaciones |
| Direct origin access | 2 - Worker | Header X-CF-API-Token requerido en producción |
| Man-in-the-middle | 1 - CF Edge | TLS 1.2+, HSTS, Full (Strict) SSL |
| Session hijacking | 3 + 4 | CF Access session 24h, Clerk session management |
| Credential stuffing | 1 + 3 | Rate limiting + CF Access email whitelist |
| Clickjacking | 4 - App | X-Frame-Options: DENY, frame-ancestors 'none' |
| MIME confusion | 4 - App | X-Content-Type-Options: nosniff |
| Timing attacks | 4 - App | crypto.timingSafeEqual() en invitaciones |
Defense in depth
Ningún mecanismo individual es infalible. La fortaleza del modelo radica en que cada capa compensa las debilidades de las otras. Un atacante debe evadir todas las capas simultáneamente para comprometer el sistema.