Saltar al contenido principal
Multi-tenant · arquitectura

Multi-tenant architecture

Capas de aislamiento en profundidad · RLS nativo de PostgreSQL + validación de tenant en aplicación (el Worker usa service_role, que puentea RLS) · 8 escenarios de ataque mitigados · 6 testing regimens (pre-commit a anual) · 8 data boundaries documentados. Tenant isolation que se PUEDE testear · NO solo afirmar.

6 isolation layers · defense-in-depth

LayerDescripciónEnforcementTesting
Row-Level Security (RLS) · Postgres nativeCada row tabla tiene `clinic_id` foreign key · RLS policies enforce `clinic_id = current_setting('app.clinic_id')` para ALL SELECT/INSERT/UPDATE/DELETEDatabase-level · imposible bypass desde application · zero trust application layerPre-commit · automated cross-tenant query tests · zero leakage allowed
Validación de tenant en aplicaciónEl Worker accede con `service_role`, que PUENTEA RLS por diseño (los webhooks de Meta/Cal/Stripe no traen JWT). Por eso cada lectura y cada escritura filtra por `clinica_id` explícitamente, con `assertClinicaId()` como guardaFiltro obligatorio en consulta · no depende de la política de BD en el camino del WorkerAuditoría V440 · 36 candidatos revisados uno a uno · cero fugas encontradas · suite de aislamiento en CI
JWT claims · tenant context propagationJWT auth incluye `clinic_id` claim · backend extrae + sets Postgres session variable `app.clinic_id` · RLS policies usanCada API call valida JWT signature + claims · NO trust path para bypassJWT manipulation attempts (signature · expired · wrong claims) rejected automated tests
Application-level tenant guardsORM/SQL wrappers en `/admin.ts` validan `clinic_id` antes de queries · defense-in-depth contra RLS misconfigurationMiddleware obligatorio TODAS endpoints autenticated · zero direct DB access app layerUnit tests negative scenarios · attempt other tenant data · expected throws
Audit logs per-tenantCada acción logged con tenant context · query own tenant logs only · cross-tenant log query attempts rejectedLog queries enforce RLS también · tenant cannot see other tenants logsAudit log isolation verified weekly automated test
Resource quotas per-tenantRate limits · storage quotas · compute usage tracked per-tenant · prevents noisy neighborCloudflare Workers Durable Objects per-tenant counter · Upstash Redis tenant-scoped rate limitsLoad tests one tenant heavy traffic · verify other tenants unaffected

8 attack scenarios · mitigated

Direct DB access bypassing app
Supabase RLS enforced database-level · API key zero-bypass · only service_role bypasses pero solo admin internal · monitoring service_role usage
JWT manipulation (clinic_id swap)
JWT signature verification mandatory + claims validation · expiry enforced · invalid JWT instant rejection · audit log attempt
SQL injection via API parameters
Parameterized queries mandatory · Zod schema validation pre-DB · no raw SQL ever from user input · WAF Cloudflare layer
Mass assignment via JSON payload
Explicit DTOs · NO accept-all body parsing · Zod schema strict mode rejects extras · clinic_id NOT settable via API · derived JWT only
Cross-tenant data via search/list endpoints
ALL list endpoints require clinic_id filter · RLS enforces si app forgets · test suite verifies negative cases
Time-based side channel (different timing per tenant)
Query plan caching per-pattern · response timing normalization · NOT exposing existence other tenants via timing differences
Fuga de credenciales de una clínica a otra
Las credenciales de terceros (token de WhatsApp · API key de Cal.com) se guardan cifradas por columna y SIEMPRE se leen filtrando por clinica_id. No hay clave por clínica: la clave de cifrado es única de entorno, así que el aislamiento lo da el filtro de tenant, no la criptografía — dicho como es
Backup restoration cross-tenant leak
Per-tenant restore via filter · RLS in restore tooling · cross-tenant restore requires explicit override audit-logged

Testing regimen · 6 cadences

  • Pre-commit · automated cross-tenant query tests · zero leakage acceptable · runs on every PR
  • Daily · automated penetration test cross-tenant access attempts · alerts si any pass · 0 expected
  • Weekly · manual review audit logs cross-tenant attempts · pattern analysis · escalate suspicious
  • Monthly · simulated breach scenario · tenant A tries access tenant B via all known attack vectors · documented results
  • Trimestral · external review (ChatGPT auditor) · architecture decisions revisited · ADR si changes pattern
  • Anual · external pen test (planned post-tracción) · specific cross-tenant scope · vendor specialized SaaS multi-tenant security

Data boundaries · 8 resources

ResourceScope
patient_messages · conversation historyEstricto por clínica · sin visibilidad cruzada · RLS + filtro obligatorio por clinica_id. Sin cifrado por columna: ver /encryption-key-management
patient_profiles · PIIPer-clinic strict · cross-clinic patient (same person 2 clinics) NOT linked · GDPR aligned
clinic_config · own settingsPer-clinic write · own clinic only · admin role required
appointments · calendar entriesPer-clinic strict · NO shared resources · Cal.com integration scoped per-clinic
billing · Stripe customer_idPer-clinic 1:1 mapping · invoices scoped · payment methods isolated
audit_logs · activityPer-clinic own logs only · super admin can query cross-tenant for support con audit trail
Cloudflare Workers env vars · secretsGlobal app-level NOT per-tenant · scoped via app logic + tenant context · NEVER tenant-specific secrets
Aggregate analytics · benchmarksCross-clinic AGGREGATED stats only (anonimizados · k≥5) · NO individual clinic data visible others
Shared DB vs dedicated · trade-offs honest

Decisión arquitectónica: shared DB con RLS vs dedicated DB per-tenant. Trade-off: shared simplifies operations · scales better · cost-effective pre-revenue. Dedicated tendría perfect isolation pero requires 1 DB por cliente · 10x operational complexity · justifiable solo Enterprise contracts con custom SLA.

Roadmap: dedicated DB tier disponible post-tracción para clientes Enterprise que lo soliciten contractualmente (HIPAA-strict · 3000+ clinics chains · ISO 27001 certified prospects). Hasta entonces: compartida con RLS + validación de tenant en cada consulta · probada en CI.

¿Tu security team necesita architecture deep-dive?

Para Enterprise procurement · architecture diagram detallado · muestra de políticas RLS · revisión del filtrado por tenant · suite de ataque entre clínicas resultados disponibles bajo NDA Enterprise.