Row Level Security (RLS) en Supabase: protege tus tablas
Supabase expone tus tablas de Postgres directamente al navegador a través de su Data API, autenticada con la anon key (publishable) pública. Las políticas de Row Level Security (RLS, seguridad a nivel de fila) son las que deciden qué filas puede leer o modificar cada usuario. Una tabla del esquema public sin RLS puede leerla — y a menudo modificarla — cualquiera que abra tu sitio.
Qué es
Todo proyecto de Supabase incluye una anon key (o sb_publishable_) pensada para vivir en tu frontend. Identifica tu proyecto, no a tu usuario: cualquiera puede copiarla de tu bundle de JavaScript y enviar sus propias peticiones a https://<project>.supabase.co/rest/v1/, saltándose tu interfaz por completo.
Row Level Security es una funcionalidad de Postgres que asocia políticas a una tabla: condiciones SQL que se evalúan para cada fila en cada SELECT, INSERT, UPDATE y DELETE. Con RLS activado y sin políticas, no se puede acceder a nada a través de la API. Cada política que agregas abre exactamente una puerta, por ejemplo "los usuarios con sesión iniciada pueden leer las filas cuyo user_id coincide con su propio id".
Por qué importa
Una política ausente o demasiado amplia es una filtración de datos completa, no un bug menor: emails de usuarios, perfiles, pedidos, mensajes privados — lo que haya en la tabla — se pueden volcar con un solo comando curl. Las herramientas de programación con IA suelen crear tablas mediante migraciones SQL, donde RLS está desactivado por defecto, y generan políticas provisionales permisivas como using (true) solo para que la app funcione.
La service_role key (sb_secret_) se salta RLS por completo. Si termina en el código del cliente, todas las políticas que escribiste dan igual: quien la tenga obtiene acceso total de lectura y escritura a tu base de datos.
Cómo solucionarlo
Ejecuta esto en el editor SQL de Supabase para listar todas las tablas del esquema public expuesto que todavía tienen RLS desactivado. El Security Advisor del dashboard reporta el mismo problema como "RLS disabled in public".
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Activa RLS en cada tabla y luego agrega una política por operación. Limita las políticas al rol authenticated y compara la columna de propietario de la fila con auth.uid(). Los INSERT y UPDATE necesitan with check para que los usuarios no puedan escribir filas que pertenecen a otra persona.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Sustituye las políticas provisionales como using (true) por condiciones reales. No bases la autorización en user_metadata — los usuarios pueden editarlo ellos mismos; usa app_metadata o una tabla de roles. Las vistas se saltan RLS salvo que se creen con security_invoker = true, y los buckets de storage necesitan sus propias políticas sobre storage.objects.
Consulta tu API exactamente como lo haría un atacante: solo con la clave pública y sin sesión de usuario. Si esto devuelve filas que no deberían ser públicas, tus políticas son demasiado abiertas.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"Usa la service_role key (sb_secret_) solo en código de servidor, como rutas de API o Edge Functions, nunca detrás de un prefijo NEXT_PUBLIC_ o VITE_. Si alguna vez llegó al navegador, rótala desde el dashboard.
Preguntas frecuentes
¿Es seguro que mi anon key de Supabase sea visible en el navegador?
Sí: la anon key (publishable) está diseñada para ser pública, pero solo si cada tabla expuesta tiene RLS activado con políticas correctas. La clave por sí misma no concede nada que tus políticas no permitan.
Activé RLS y ahora mi app devuelve resultados vacíos. ¿Está roto?
Es RLS funcionando como debe: sin políticas, se deniega cada fila. Agrega políticas para las operaciones que tu app necesita, limitadas a los usuarios correctos, en lugar de volver a desactivar RLS.
¿AppSafe prueba mis políticas de RLS?
AppSafe no inicia sesión en tu base de datos, así que no puede evaluar tus políticas: usa las comprobaciones con SQL y curl de arriba. Lo que sí hace es analizar tu JavaScript en busca de claves de Supabase. Si reporta una, decodifica la parte central del JWT y revisa el claim role: anon es lo esperado en el navegador; service_role es una filtración crítica.
¿Tu app está afectada?
AppSafe verifica este y docenas de problemas más en un solo escaneo gratis.
Escanear mi app gratisGuías relacionadas
Firebase Security Rules: deja de dejar tu base de datos abierta
Tu config de Firebase es pública por diseño. Tus reglas deciden quién ve los datos.
API keys expuestas en el JavaScript del cliente
Todo lo que va en tu bundle es público. Trátalo como tal.
Backups de base de datos expuestos: qué pasa cuando /backup.sql es accesible
Un volcado .sql accesible es la entrega completa de toda tu base de datos.