Saltar al contenido principal
Multi-tenant · arquitectura

Multi-tenant architecture

6 isolation layers defense-in-depth · RLS PostgreSQL native + pgsodium per-tenant encryption · 8 attack scenarios mitigated · 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
Per-tenant encryption keys · pgsodiumPII columns (patient_name · phone · medical_history · conversation_messages) encriptados con key derivada por tenant · pgsodium secret box per-tenant uniqueDatabase stored encryption · application no acceso plaintext sin descrypt explicitCross-tenant decrypt attempts logged + rejected · audit trail mandatory
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
Encryption key reuse cross-tenant
Per-tenant key derivation via tenant_id as KDF input · keys NEVER reused · key rotation per-tenant on-demand
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 historyPer-clinic strict · NO cross-clinic visibility · RLS enforced · pgsodium per-tenant encrypted
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: shared con RLS + per-tenant encryption · tested rigurosamente.

¿Tu security team necesita architecture deep-dive?

Para Enterprise procurement · architecture diagram detallado · RLS policies sample · pgsodium implementation review · cross-tenant attack test suite resultados disponibles bajo NDA Enterprise.