blackdark
Herramientassupabaserlsrow level securityseguridadpostgresvibe codingclaude

Supabase RLS: cómo cerrar tu base de datos (seguridad por fila paso a paso)

En septiembre de 2026, UpGuard encontró 16.326 bases de datos de Supabase que cualquiera podía leer. Cómo ver qué tablas tienes «Unrestricted», activar RLS, crear las políticas básicas con auth.uid() y el prompt para que tu IA lo haga sin romper la app.

Por BlackdarkActualizado el 7 min de lectura
Ir directo a los pasos

Un desconocido entra en tu app y abre todos los cofres. Sin contraseña. No es una película: el 25 de septiembre de 2026, la empresa de seguridad UpGuard publicó que había encontrado 16.326 bases de datos de Supabase con tablas que cualquiera podía leer. En más de la mitad había datos personales.

No fue un fallo de Supabase. Faltaba una regla: RLS, la seguridad por fila. Con ella, cada usuario abre solo su cofre. Sin ella, el que tenga la clave pública de tu app (y la tiene cualquiera que abra tu web) puede leer la tabla entera.

Atención

La trampa de la IA. Las tablas que creas a mano desde el panel de Supabase traen RLS activado. Pero las que crea la IA con SQL, con migraciones o con otra herramienta pueden venir sin él. Si tu app la ha montado Claude, Cursor, Lovable o Bolt, revísala hoy.

Qué es RLS, en una frase

Tu base de datos es un almacén lleno de cofres (las filas). La clave pública de Supabase (la anon o la nueva sb_publishable_) abre la puerta del almacén. Eso es normal: tiene que ir en tu web para que funcione. RLS es el guardia de dentro: mira quién eres y solo te deja abrir tus cofres.

Las reglas que sigue el guardia se llaman políticas. Por ejemplo: «cada usuario solo puede leer las filas donde user_id es su propio id».

Cerrar los cofres en 4 pasos

  1. Busca las tablas abiertas

    En el Table Editor, las que pongan «Unrestricted» no tienen RLS.

  2. Activa RLS

    Un clic en el panel o una línea de SQL por tabla.

  3. Crea las políticas

    Cada usuario solo lee y edita sus filas, con auth.uid().

  4. Guarda la clave secreta

    La service_role solo vive en tu servidor. Nunca en el navegador.

1. Mira qué tablas están «Unrestricted»

  1. Entra en supabase.com y abre tu proyecto.
  2. Ve al Table Editor (el icono de tabla en la barra lateral).
  3. Mira la lista de tablas de la izquierda. Si alguna lleva la etiqueta roja «Unrestricted», no tiene RLS: es un cofre abierto.

También puedes abrir el Security Advisor (en Advisors), que avisa de las tablas públicas sin RLS. Y si prefieres SQL, pega esto en el SQL Editor para ver todas de golpe:

-- Tablas del esquema public y si tienen RLS activado
select tablename, rowsecurity as rls_activado
from pg_tables
where schemaname = 'public'
order by rowsecurity, tablename;

Las que salgan con false son las que tienes que cerrar.

2. Activa RLS (sin romper nada todavía)

Antes de pulsar el botón, un aviso importante: RLS bloquea todo por defecto. Si lo activas y no creas ninguna política, tu app dejará de ver los datos de esa tabla. No es un error: el guardia no tiene órdenes y no deja pasar a nadie.

Por eso, lo bueno es activar RLS y crear las políticas en el mismo paso (el paso 3). Se puede hacer de dos formas:

  • Desde el panel: en Database → Policies, busca la tabla y pulsa Enable RLS.
  • Con SQL (en el SQL Editor):
alter table public.notas enable row level security;

Cambia notas por el nombre de tu tabla. Repite con cada tabla que estaba «Unrestricted».

3. Las políticas básicas: cada usuario, sus filas

El caso más común: una tabla donde cada fila es de un usuario (sus notas, sus pedidos, su perfil). Para eso, la tabla necesita una columna que diga de quién es cada fila. Lo normal es user_id, que guarda el id del usuario que la creó.

auth.uid() es una función de Supabase que devuelve el id del usuario que ha iniciado sesión y hace la consulta. Las políticas comparan ese id con el user_id de cada fila:

-- 1) Activa RLS en la tabla
alter table public.notas enable row level security;

-- 2) Leer: cada usuario ve solo sus filas
create policy "Cada usuario lee sus notas"
on public.notas for select
to authenticated
using ( (select auth.uid()) = user_id );

-- 3) Crear: solo puede crear filas a su nombre
create policy "Cada usuario crea sus notas"
on public.notas for insert
to authenticated
with check ( (select auth.uid()) = user_id );

-- 4) Editar: solo sus filas, y sin pasarlas a otro usuario
create policy "Cada usuario edita sus notas"
on public.notas for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );

-- 5) Borrar: solo sus filas
create policy "Cada usuario borra sus notas"
on public.notas for delete
to authenticated
using ( (select auth.uid()) = user_id );

-- 6) Índice para que las consultas sigan siendo rápidas
create index if not exists notas_user_id_idx on public.notas (user_id);

Qué significa cada parte, sin jerga:

  • to authenticated: la regla solo vale para usuarios con sesión iniciada. Un visitante sin cuenta no ve nada.
  • using: qué filas puede ver o tocar. Aquí, las suyas.
  • with check: qué datos puede escribir. Así nadie crea filas a nombre de otro.
  • (select auth.uid()): va entre paréntesis con select porque Supabase lo recomienda: Postgres lo calcula una vez por consulta en vez de una vez por fila, y va más rápido.

Consejo

¿Tienes datos que sí deben ser públicos? Por ejemplo, la lista de productos de tu tienda. Para eso hay una política de solo lectura para todos: create policy "Productos visibles" on public.productos for select to anon, authenticated using ( true );. Úsala solo con datos que no te importe que vea cualquiera, y nunca para escribir.

Nota

Un paso más (opcional). La documentación de Supabase recomienda además dejar a cada rol solo los permisos que necesita en cada tabla (los «grants»): quitarlos todos con revoke y devolver solo los que usa tu app. Es una segunda capa de seguridad, pero si te equivocas, la app falla con el error 42501. Si no sabes qué permisos usa tu app, pídeselo a tu IA con el prompt de abajo y pruébalo antes en una copia.

4. La clave service_role nunca va en el navegador

Supabase tiene dos tipos de clave:

ClaveDónde va¿Respeta RLS?
Pública: anon o sb_publishable_…Web, app del móvil, cualquier sitioSí: solo ve lo que dejan tus políticas
Secreta: service_role o sb_secret_…Solo en tu servidor (API, Edge Functions, tareas programadas)No: se salta RLS y ve todo

La clave secreta es la llave maestra del almacén. Si acaba en el código del navegador, da igual lo bien que tengas RLS: quien la encuentre lo abre todo. Revisa que:

  • No esté en ninguna variable que empiece por NEXT_PUBLIC_ o VITE_ (esas acaban en el código que descarga el usuario).
  • No esté escrita en el código ni subida a GitHub (aquí tienes cómo proteger tus claves).
  • Si alguna vez estuvo en público, la anules y crees otra en Settings → API Keys.

El prompt para pedírselo a tu IA sin romper la app

Si tu app la ha hecho Claude Code, Cursor u otra IA, lo más fácil es que ella misma revise y cierre las tablas. Este prompt le obliga a hacerlo por partes y enseñándote el plan antes, para que la app no se quede sin datos:

Prompt · Activar RLS en Supabase sin romper la app
Revisa la seguridad de la base de datos Supabase de este proyecto. NO cambies nada todavía. 1) Haz una lista de todas las tablas del esquema public y dime cuáles tienen RLS (Row Level Security) desactivado. 2) Para cada tabla, busca en el código de la app cómo se usa: quién la lee, quién escribe y si hace falta tener sesión iniciada. 3) Propónme las políticas para cada tabla: si las filas son de un usuario, que cada usuario solo pueda leer, crear, editar y borrar las suyas usando (select auth.uid()) = user_id, con to authenticated; si son datos públicos (por ejemplo, productos), solo lectura para todos y escritura para nadie desde el navegador. Si a una tabla le falta la columna user_id, dímelo y propón cómo añadirla sin perder datos. 4) Comprueba que la clave service_role o sb_secret_ no aparece en ningún archivo del navegador ni en variables NEXT_PUBLIC_ o VITE_. Enséñame el plan en una tabla (tabla, problema, solución) y espera mi OK. Cuando lo apruebe, escribe todo en un archivo de migración SQL, activando RLS y creando las políticas de cada tabla en el mismo paso, añade un índice en cada columna user_id y dime cómo probar que un usuario no puede ver los datos de otro.

Consejo: pruébalo primero en una copia del proyecto o en una rama de Supabase. Después entra con dos cuentas de prueba y comprueba que cada una solo ve lo suyo.

Checklist rápido

  • Ninguna tabla del esquema public pone «Unrestricted» en el Table Editor.
  • Cada tabla con datos de usuarios tiene políticas con auth.uid().
  • Las tablas públicas solo tienen permiso de lectura.
  • La clave service_role (o sb_secret_) solo está en el servidor.
  • Has probado con dos cuentas que una no ve los datos de la otra.

Rápido y seguro no son lo mismo. Diez minutos hoy te ahorran ser el próximo de la lista.

Fuentes: informe de UpGuard sobre Supabase, TechCrunch (25-sep-2026), Row Level Security en la documentación de Supabase, cómo proteger tu API y claves de API de Supabase.

FAQ

Row Level Security (seguridad por fila) es una función de Postgres, la base de datos que usa Supabase. Con ella activada, cada consulta pasa por unas reglas llamadas políticas que deciden qué filas puede ver o cambiar quien pregunta. Así cada usuario solo abre su propio cofre.

Entra en tu proyecto de Supabase y abre el Table Editor. Las tablas del esquema public que no tienen RLS aparecen con la etiqueta roja «Unrestricted». También puedes mirar la página de políticas (Database, Policies) o el Security Advisor, que avisa de las tablas públicas sin RLS.

Porque RLS bloquea todo por defecto. Si activas RLS y no creas ninguna política, la clave pública no ve nada en esa tabla. No es un fallo: falta decir quién puede leer qué. Crea las políticas en el mismo paso y la app vuelve a funcionar, pero solo con los datos de cada usuario.

No. Está pensada para ir en el navegador o en la app, así que cualquiera puede verla. Lo que protege tus datos es RLS. La que sí es secreta es la clave service_role (o las nuevas sb_secret_): se salta RLS y solo puede usarse en tu servidor.

No. UpGuard habla de configuraciones mal hechas por quien monta la app: tablas sin RLS, políticas que dejan pasar demasiado o claves usadas donde no tocaba. Supabase lo describe como responsabilidad compartida: la plataforma da las herramientas y cada proyecto tiene que configurarlas.

Sigue profundizando en el mismo tema.

Compartir
Newsletter

Recibe las próximas guías en tu correo

Ideas y recursos de IA y marketing, sin relleno. Lo que funciona y cómo aplicarlo.

Ideas y recursos sin spam. Cancela cuando quieras.

¿Te sirve esta guía?

Suscríbete