El botón verde que te miente: la vulnerabilidad más peligrosa en Supabase
Tu base de datos no está rota. De hecho, Postgres está funcionando exactamente como le pediste. Ese es el verdadero problema.
Hay una falsa sensación de calma cuando trabajas con Supabase. Creas tus tablas, vas al panel de control, haces clic en "Enable RLS" y la interfaz te premia con un switch verde brillante. En ese instante, el 90% de los desarrolladores asume que la casa está cerrada con llave. Duermen tranquilos. Yo también lo hice hace unos años, hasta que un cliente de e-commerce en Bogotá me llamó un domingo a las 3:00 AM porque un usuario curioso descubrió que podía listar los pedidos, direcciones y tarjetas enmascaradas de otros 42,000 clientes con una sola petición fetch desde la consola del navegador.
No hubo inyección SQL. No vulneraron los servidores de Supabase. Nadie hackeó AWS. El sistema respondió con éxito porque nosotros mismos le dimos permiso de filtrar todo sin darnos cuenta. El silencio de Postgres es letal: cuando una política falla por omisión, la base de datos no arroja un error 500; simplemente entrega los datos con una sonrisa.
El error de confundir un filtro de frontend con seguridad
El pecado original de casi todo proyecto que reviso en GuardLabs empieza en el cliente. Veo código como este todos los días:
const { data } = await supabase.from('invoices').select('*').eq('user_id', session.user.id);
El desarrollador cree que porque escribió .eq('user_id', session.user.id) en su código de React o Flutter, el sistema ya es seguro. Eso no es seguridad; es cosmética de interfaz. Si un usuario malicioso abre Postman, copia su JWT válido y manda un GET /rest/v1/invoices?select=* sin el parámetro eq, Supabase le va a devolver cada una de las filas de la tabla si la política en Postgres está mal planteada o incompleta.
La regla de oro de Row Level Security en Supabase Postgres es simple: a la base de datos no le importa qué librerías usas en el frontend. Si la política RLS no bloquea la fila a nivel de motor relacional, la fila es pública para cualquiera con una clave anon o un JWT autenticado.
Las tres fugas silenciosas que nunca saltan en tus tests automáticos
En el trabajo diario de auditoría, los desastres rara vez ocurren por ataques sofisticados. Suceden por tres descuidos conceptuales muy específicos:
1. Políticas permisivas en cascada: Creas una política para SELECT con auth.uid() = user_id, pero olvidas definir políticas explícitas para UPDATE o DELETE. En Postgres, si habilitas RLS sin políticas para una acción específica, el acceso se niega por defecto. Pero el peligro real ocurre cuando alguien crea una política global genérica FOR ALL USING (true) pensando que luego la limitará, y esa regla termina en producción. Un solo USING (true) destruye el aislamiento de todo tu sistema.
2. Funciones con SECURITY DEFINER sin aislamiento: Supabase te permite escribir funciones en PL/pgSQL y marcarlas como SECURITY DEFINER. Esto hace que la función se ejecute con los privilegios del creador (usualmente postgres o service_role), saltándose todas las políticas de RLS. Si dentro de esa función aceptas un parámetro directo como target_user_id sin validar internamente si el emisor tiene derecho a modificar a ese usuario, acabas de abrir una puerta trasera del tamaño de un edificio.
3. Vistas que olvidan heredar RLS: Creas una vista SQL para consolidar reportes o métricas de usuarios. Hasta versiones recientes de Postgres, las vistas no aplicaban RLS automáticamente a menos que las declararas con WITH (security_invoker = true). Miles de proyectos hoy tienen tablas seguras pero exponen esas mismas tablas desnudas a través de vistas públicas expuestas en la API de Supabase.
Cómo auditar tu base de datos antes de que sea tarde
Dejen de probar sus aplicaciones usando únicamente su propia UI con su usuario de pruebas. Si quieren saber si su arquitectura resiste, necesitan ensuciarse las manos desde afuera.
Abran su terminal. Tomen la anon key pública y hagan peticiones HTTP crudas contra su API de Supabase. Intenten hacer un PATCH a la tabla de perfiles cambiando el rol a admin. Intenten hacer un DELETE a registros de otra organización enviando un user_id falso en el cuerpo de la petición. Si Postgres les responde con un código 200 OK o 204 No Content, tienen una brecha abierta.
Escribir buenas políticas requiere tiempo, entender cómo interactúan los roles anon y authenticated, y saber medir el costo de rendimiento de subconsultas anidadas dentro de un CHECK.
Una mirada profesional a su infraestructura
Cuando un proyecto escala o maneja datos médicos, balances financieros o información sensible de clientes, una fuga de datos destruye la reputación en minutos. Arreglarlo después de una notificación de filtración cuesta diez veces más que estructurarlo bien desde el inicio.
Si necesitan la mirada externa de un especialista para revisar sus políticas, cerrar brechas lógicas y optimizar sus consultas bajo roles reales, en GuardLabs ofrezco un servicio de supabase rls audit freelance para equipos que necesitan validar su arquitectura en producción antes de salir al mercado masivo. Revisamos cada tabla, vista y función para que su base de datos sea tan sólida como ustedes creen que es.