Separación de datos por organización aplicada a nivel de base de datos.
Row Level Security (RLS) en todas las tablas de cliente: aunque dos empresas compartan infraestructura, jamás ven datos de la otra. La separación la hace Postgres, no el código.
Esta es la auto-evaluación de AlgizSec contra el estándar OWASP ASVS (Application Security Verification Standard). Cada control apunta a evidencia técnica verificable en nuestro código. Sin maquillaje.
Row Level Security (RLS) en todas las tablas de cliente: aunque dos empresas compartan infraestructura, jamás ven datos de la otra. La separación la hace Postgres, no el código.
Supabase Auth con PKCE. Los endpoints que actúan por un usuario validan el token con auth.getUser(token) antes de procesar y responden 401 si no sirve. Son públicos por diseño el chat comercial, la salud del sistema y los webhooks, que validan su propia firma.
autoRefreshToken: true + flowType: pkce. Supabase Auth rota el refresh token en cada renovación. La sesión se guarda en el localStorage del navegador.
Rol admin codificado en app_metadata del JWT y validado por políticas RLS de Postgres. Aunque la aplicación falle, RLS rechaza el acceso.
El chat comercial limita el cuerpo a 16 KB, quita caracteres de control y tiene un detector heurístico de prompt injection que responde sin gastar cuota de IA; el chat de la app limita a 100 KB. Los demás endpoints validan sus campos uno a uno, sin un límite de tamaño común.
Datos persistidos en Supabase, que cifra en reposo con AES-256 por defecto, sobre infraestructura de AWS. Los archivos, igual. La aplicación no agrega un cifrado propio.
Cada acción se firma con HMAC-SHA256 (id|user|ip|metadata|timestamp) al insertarse. Triggers de Postgres rechazan UPDATE, DELETE y TRUNCATE sobre audit_logs: comprobado intentando modificarla con permisos totales de base de datos. La clave de firma la custodia AlgizSec, así que la bitácora es inalterable para cualquier usuario de la plataforma; el anclaje ante un tercero independiente está en desarrollo.
Service role key solo en el backend. Anon key con permisos limitados por RLS. La mayoría de los endpoints responde un mensaje genérico ante un error interno; algunos todavía devuelven el mensaje del proveedor, y se están cerrando.
Strict-Transport-Security con preload, includeSubDomains y max-age de 2 años. En /app, /auditoria, /transparencia, /seguridad, /security, /cumplimiento y /ley-21663 —incluida ésta— el script-src solo admite el propio dominio y Google. En la portada, /privacidad y /terminos-y-condiciones admite unsafe-inline y unsafe-eval. frame-ancestors self, X-Frame-Options SAMEORIGIN y Referrer-Policy strict-origin-when-cross-origin. La versión de TLS la negocia Vercel.
DOMPurify sobre todo HTML que se pinta con dangerouslySetInnerHTML, y el CSP estricto de /app. Sin SQL concatenado: las consultas van por el SDK de Supabase o por funciones de Postgres con parámetros.
Límite de peticiones con contador en Postgres, compartido entre instancias, en los endpoints que actúan por un usuario (17 de 26); no en los webhooks, los cron ni la salud del sistema. Cortacircuitos para Twilio y Resend: 3 fallos en 60 s abren el circuito, y los envíos quedan en cola en Inngest. El estado del circuito vive en cada instancia, no es compartido.
Archivos suben directo del navegador a Supabase Storage (no pasan por servidor de aplicación). Bucket protegido con RLS por user_id. Visualización vía signed URL con expiración de 1 hora.
El patrón —límite de peticiones, token Bearer, validación de campos y respuesta sin detalles internos— lo siguen los endpoints que actúan por un usuario, no todos: la salud del sistema es pública y los webhooks validan su propia firma en vez de un token.
API keys solo en variables de entorno del backend. Al cliente llegan solo valores públicos por diseño: la anon key de Supabase (limitada por RLS), los identificadores de Google y el DSN de Sentry. Un pre-commit bloquea .env, .pem e id_rsa/id_ecdsa en el repositorio.
Si descubre una vulnerabilidad en AlgizSec, repórtela de forma privada a security@algizsec.com. Respondemos en menos de 72 horas y reconocemos públicamente a quienes reportan en buena fe. No tomamos acciones legales contra investigación responsable de seguridad.