RLS en Supabase: ¿tu app construida con IA está filtrando datos?

Las apps construidas con IA suelen salir con row level security débil en Supabase. Cómo revisar la tuya en cinco minutos y cómo es una policy correcta.

En esta página · 5 secciones
  1. ¿Por qué la base de datos necesita row level security?
  2. ¿Qué tan común es el problema?
  3. ¿Cómo revisas tu app en cinco minutos?
  4. ¿Cómo es una policy correcta?
  5. ¿Qué errores deberías buscar?

Puntos clave

  • Una app construida sobre Supabase se comunica con la base de datos directamente desde el navegador. El row level security (RLS) es lo único que impide que un usuario lea los datos de todos los demás.
  • Cuando unos investigadores analizaron 1.645 proyectos de Lovable en 2025, 170 de ellos tenían fallas de RLS que exponían datos a cualquiera.
  • Puedes probar tu propia app en cinco minutos con la clave pública y una solicitud. Si devuelve filas que no son tuyas, corrígelo antes que cualquier otra cosa.

La mayoría de las apps construidas con Lovable, Bolt y herramientas de IA similares usan Supabase como backend. Es una buena elección: una base de datos Postgres real, autenticación y una API listas para usar. Pero trae una regla que las herramientas de IA no siempre siguen, y cuando no la siguen, el resultado no es un bug. Es una filtración de datos.

Este post explica esa regla, cómo comprobar si tu app la cumple y cómo es una policy correcta. Forma parte de la guía para dejar un prototipo construido con IA listo para usuarios reales.

¿Por qué la base de datos necesita row level security?

Porque el frontend de tu app se comunica con ella directamente. Una app con Supabase incluye una publishable key (los proyectos más antiguos la llaman anon key) dentro del JavaScript que descarga cada visitante. Eso es intencional: Supabase dice que la publishable key se puede exponer sin problema, y agrega que “Row Level Security decide a qué puede llegar este cliente, así que habilítalo en cada tabla antes de desplegar” (Supabase).

En otras palabras, la clave solo identifica tu proyecto. Lo que un visitante puede leer o modificar lo deciden por completo las policies de RLS de cada tabla. Sin ellas, la documentación de Supabase es tajante: “Una tabla en un schema expuesto sin RLS puede ser leída y escrita por cualquier rol que tenga un grant sobre ella.”

¿Qué tan común es el problema?

Lo bastante común como para tener un CVE. En marzo de 2025, el investigador Matt Palmer analizó 1.645 proyectos construidos con Lovable y encontró 170 de ellos, cerca del 10%, con un RLS inadecuado: 303 endpoints expuestos, encontrados solo mirando las páginas de inicio de los proyectos (declaración sobre el CVE-2025-48757). Los datos expuestos incluían nombres y correos, claves de API de servicios de terceros y detalles de pagos y suscripciones (CVE-2025-48757).

Desde entonces Lovable agregó un escáner de seguridad, pero el riesgo de fondo no es exclusivo de una herramienta. Cualquier app en la que el navegador se comunica con Supabase depende de que las policies estén bien, y las herramientas de IA escriben esas policies igual que escriben todo lo demás: hasta que la app funciona.

¿Cómo revisas tu app en cinco minutos?

1. Lista las tablas sin RLS. En el editor SQL de Supabase, ejecuta:

select tablename, rowsecurity
from pg_tables
where schemaname = 'public';

Cualquier fila con rowsecurity = false es una tabla que cualquiera con tu clave pública puede leer y escribir.

2. Pruébalo como un desconocido. Toma la publishable key de tu sitio (está en el JavaScript) y consulta una tabla directamente, sin iniciar sesión:

curl 'https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*' \
  -H "apikey: YOUR_PUBLISHABLE_KEY"

Si esto devuelve perfiles de otras personas, la solicitud de cualquier otra persona también lo hará.

3. Pruébalo como otro usuario. Inicia sesión como el usuario A, copia el ID de un registro que pertenece al usuario B y pídelo. No deberías recibir nada.

4. Lee el Security Advisor. El dashboard de Supabase marca las tablas sin RLS y otros errores comunes. No detecta una policy demasiado permisiva, y por eso importan los pasos 2 y 3.

¿Cómo es una policy correcta?

Para una tabla en la que cada fila pertenece a un usuario, el patrón es: RLS activado y una policy por acción que compare al dueño de la fila con el usuario que inició sesión.

alter table public.projects enable row level security;

create policy "Owners read their projects"
on public.projects for select
to authenticated
using ((select auth.uid()) = user_id);

create policy "Owners create their projects"
on public.projects for insert
to authenticated
with check ((select auth.uid()) = user_id);

Agrega policies de update y delete de la misma forma, y crea un índice en user_id. La propia recomendación de Supabase es indexar las columnas que usan tus policies y envolver auth.uid() en un select, lo que en sus pruebas redujo una consulta de 179 ms a 9 ms (Supabase).

¿Qué errores deberías buscar?

  • using (true). Una policy que permite todo es lo mismo que no tener policy. Las herramientas de IA la escriben para que desaparezca un error.
  • RLS desactivado para “arreglar” la app. Activar RLS sin policies bloquea todo el acceso a través de la API, así que la app se rompe. El arreglo rápido que viene después suele ser desactivarlo de nuevo.
  • Insert y update sin with check. Entonces los usuarios pueden crear filas que dicen pertenecer a otra persona.
  • Roles guardados donde los usuarios pueden editarlos. Una columna is_admin en la propia fila de perfil del usuario, con una policy de update que le deja editar su fila, convierte a cualquiera en admin.
  • La clave secreta en el frontend. La clave secreta (service role) de Supabase se salta todas las policies. Si está en el navegador, RLS no importa. Más sobre eso en el próximo post de esta serie.

Preguntas frecuentes

¿Es seguro poner la anon key de Supabase en mi frontend?

Sí, la publishable key (anon) está diseñada para ser pública. Solo es segura porque row level security controla a qué puede llegar, así que cada tabla expuesta a través de la API necesita RLS habilitado y policies que correspondan a quién debe ver cada fila.

¿Cómo sé si mi app de Lovable tiene el problema de RLS?

Comprueba que RLS esté habilitado en todas las tablas del schema public y luego consulta una tabla solo con la publishable key y sin iniciar sesión. Si recibes filas que deberían ser privadas, tus policies faltan o son demasiado permisivas.

¿Habilitar RLS rompe mi app?

Puede pasar, al principio. Con RLS activado y sin policies, la API no devuelve nada, así que las páginas que leen esa tabla quedan vacías. Esa es la falla segura. La solución es agregar policies para cada acción, no volver a desactivar RLS.

Fuentes

  1. Supabase — Row Level Security
  2. Supabase — Understanding API keys
  3. Supabase — RLS performance and best practices
  4. Matt Palmer — Statement on CVE-2025-48757
  5. Matt Palmer — CVE-2025-48757
AIRow-level securitySecuritySupabase

Filipe Eduardo

Ingeniero de Software Sénior. Siete años creando productos web y móviles de punta a punta y liderando los equipos que los entregan.

Sígueme

¿Quieres hablar
de esto?

Trabajo en problemas así todos los días. Cuéntame el tuyo.

Contáctame