FixVibe

// docs / baas security / supabase rls scanner

Escáner de RLS de Supabase: detecta tablas con seguridad a nivel de fila ausente o rota

La seguridad a nivel de fila (RLS) es lo único que se interpone entre los datos de tus clientes e internet cuando publicas una aplicación respaldada por Supabase. Las herramientas de codificación con IA generan código con forma de RLS que compila, se publica y filtra datos en silencio — tablas creadas sin RLS habilitado, políticas que leen pero nunca restringen, predicados que comparan una columna consigo misma. Este artículo muestra qué puede probar un escáner de RLS de Supabase desde el exterior, las cuatro formas de RLS rota que aparecen en aplicaciones generadas por IA y cómo escanear tu propio despliegue en menos de un minuto.

Qué puede probar un escaneo de RLS externo

Un escaneo de RLS pasivo se ejecuta contra el endpoint PostgREST que Supabase expone en https://[project].supabase.co/rest/v1/. Solo utiliza la clave anon publicable — la misma que usa tu navegador — y sondea los metadatos de listado de tablas, las lecturas anónimas y las escrituras anónimas. Nunca se autentica como usuario y nunca toca los privilegios de rol de servicio. Cualquier cosa que pueda hacer, un atacante no autenticado en internet también puede hacerla.

Desde fuera de la base de datos, un escáner puede confirmar lo siguiente con alta confianza:

  • RLS está deshabilitado en una tabla. PostgREST devuelve filas para un SELECT anónimo cuando RLS está desactivado o cuando una política lo permite. Cualquiera de los dos casos es un hallazgo.
  • El rol anónimo puede listar tablas. Un GET /rest/v1/ con la clave anon devuelve el esquema OpenAPI de toda tabla sobre la que el rol anon tenga cualquier privilegio. Las aplicaciones generadas por IA con frecuencia otorgan USAGE sobre el esquema y SELECT sobre cada tabla, lo que expone el mapa completo del esquema incluso cuando RLS niega las lecturas reales.
  • El rol anónimo puede insertar. Un POST de sondeo con una conjetura sobre la forma de las columnas tendrá éxito si RLS no tiene una política INSERT que lo niegue — incluso si SELECT está bloqueado.
  • La clave de rol de servicio está en el bundle del navegador. Adyacente a RLS: si un escáner encuentra SUPABASE_SERVICE_ROLE_KEY o cualquier JWT con role: service_role en el bundle de JavaScript, RLS es irrelevante — quien posea esa clave puede saltarse cualquier política.

Qué no puede probar un escaneo externo

Sé honesto sobre los límites del escáner. Un escaneo de RLS externo no puede leer tu tabla pg_policies, tus archivos de migración ni el predicado exacto de ninguna política. Infiere a partir del comportamiento de caja negra, lo que significa que a veces reportará un hallazgo que resulta ser datos públicos intencionados (una tabla de newsletter de marketing, un catálogo público de productos). El reporte de FixVibe los marca como confianza media cuando el escáner no puede determinar la intención — revisa el nombre de la tabla y decide.

Las cuatro formas de RLS rota que producen las herramientas de IA

Cuando apuntas Cursor, Claude Code, Lovable o Bolt a Supabase, los mismos cuatro patrones de RLS rota emergen en miles de aplicaciones. Cada uno pasa la verificación de tipos, compila y se publica:

Forma 1: RLS nunca habilitado

El modo de fallo más común. La migración crea la tabla pero el desarrollador (o la herramienta de IA) olvida ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST sirve felizmente la tabla completa a cualquiera con la clave anon. Solución: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE no es opcional — sin él, el propietario de la tabla (y cualquier rol con propiedad de tabla) se salta RLS.

sql
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE  ROW LEVEL SECURITY;

Forma 2: RLS habilitado, sin políticas

Un fallo más sutil. RLS está habilitado pero no se han escrito políticas. El predeterminado en PostgreSQL es denegar, por lo que los usuarios autenticados no ven nada — y el desarrollador añade USING (true) para que la aplicación funcione, lo que permite que todos lo lean todo. Solución: escribe una política que acote por auth.uid(): CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); y una política INSERT/UPDATE/DELETE correspondiente.

sql
CREATE POLICY "select_own"
  ON public.[name]
  FOR SELECT
  USING (auth.uid() = user_id);

Forma 3: La política compara la columna consigo misma

A copy-paste artefact. The developer writes <code>USING (user_id = user_id)</code> — which is always true — instead of <code>USING (auth.uid() = user_id)</code>. Type-checks pass; the policy permits every row. <strong>Fix:</strong> always compare a column to a function call (<code>auth.uid()</code>, <code>auth.jwt()->>'org_id'</code>, etc.), never to itself or to a constant.

Forma 4: Política en SELECT pero no en INSERT/UPDATE

El desarrollador bloquea las lecturas pero olvida las escrituras. Las políticas RLS son por comando. FOR SELECT solo protege las lecturas; un cliente anónimo todavía puede INSERT si ninguna política lo niega. Solución: escribe una política por comando, o usa FOR ALL con cláusulas explícitas USING y WITH CHECK.

Cómo funciona el escáner de RLS de Supabase de FixVibe

La verificación baas.supabase-rls se ejecuta en tres etapas, cada una con niveles de confianza explícitos:

  1. Etapa 1 — identificación. El escáner rastrea la aplicación desplegada, analiza su bundle de JavaScript y extrae la URL del proyecto Supabase y la clave anon de la configuración en tiempo de ejecución. Sin adivinanzas de DNS, sin fuerza bruta — lee lo mismo que lee el navegador.
  2. Etapa 2 — descubrimiento del esquema. Un único GET /rest/v1/ con la clave anon devuelve el esquema OpenAPI de cada tabla que el rol anon pueda ver. El escáner registra los nombres de las tablas pero no lee los datos de las filas en esta etapa.
  3. Etapa 3 — sondeos de lectura y escritura. Para cada tabla descubierta, el escáner emite un SELECT anónimo con limit=1. Si se devuelven filas, RLS es permisivo. El escáner se detiene ahí — no enumera filas, no pagina, no modifica datos. Los sondeos INSERT están restringidos por la verificación de propiedad de dominio y opt-in explícito; nunca se ejecutan contra objetivos no verificados.

Cada hallazgo se entrega con la URL exacta de la solicitud, el estado de la respuesta, la forma de la respuesta (solo cabecera) y el nombre de la tabla. El prompt de corrección con IA al final del hallazgo es un bloque SQL listo para copiar y pegar que ejecutas en el editor SQL de Supabase.

Qué hacer cuando el escáner encuentra algo

Cada hallazgo de RLS es una emergencia en tiempo de ejecución. Los endpoints PostgREST públicos son escaneados por atacantes en cuestión de minutos. La secuencia de remediación es mecánica:

  1. Audita cada tabla. Ejecuta SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; en el editor SQL de Supabase. Cualquier fila con rowsecurity = false es un problema.
  2. Habilita RLS en cada tabla pública. Por defecto, aplica ENABLE ROW LEVEL SECURITY y FORCE ROW LEVEL SECURITY en cada tabla creada — conviértelo en una plantilla de migración.
  3. Escribe políticas comando por comando. No uses FOR ALL USING (true). Escribe políticas explícitas para SELECT, INSERT, UPDATE, DELETE — cada una acotada a auth.uid() o a una columna de org-id desde auth.jwt().
  4. Verifica con una segunda cuenta. Regístrate como un usuario diferente, intenta leer los registros de otro usuario a través de la API REST directamente. Si la respuesta es 200, la política está rota.
  5. Vuelve a escanear. Tras aplicar la corrección, ejecuta de nuevo un escaneo de FixVibe contra la misma URL. El hallazgo baas.supabase-rls debería desaparecer.
sql
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM   pg_tables
WHERE  schemaname = 'public'
ORDER  BY rowsecurity, tablename;

Cómo se compara esto con otros escáneres

La mayoría de herramientas DAST genéricas (Burp Suite, OWASP ZAP, Nessus) no saben qué es PostgREST. Rastrearán tu aplicación, ignorarán la ruta /rest/v1/ y reportarán las páginas HTML que sí entienden. Snyk y Semgrep son herramientas de análisis estático — encuentran archivos de migración en tu repositorio con llamadas RLS faltantes, pero no pueden demostrar que la base de datos desplegada esté mal configurada. FixVibe ocupa ese hueco: pasivo, consciente de BaaS, enfocado en lo que un atacante no autenticado puede demostrar desde la URL pública.

Preguntas frecuentes

¿Leerá o modificará el escáner mis datos?

No. Los escaneos pasivos emiten como máximo un SELECT ... limit=1 por tabla descubierta para confirmar si RLS permite lecturas anónimas. El escáner registra la forma de la respuesta, no el contenido de las filas. Los sondeos INSERT, UPDATE y DELETE están restringidos por la verificación de propiedad de dominio y nunca se ejecutan contra objetivos no verificados.

¿Funciona esto si mi proyecto de Supabase está pausado o en un dominio personalizado?

Los proyectos pausados devuelven 503 en cada solicitud — el escáner reporta el proyecto como inalcanzable. Los dominios personalizados funcionan siempre que la aplicación desplegada cargue el SDK cliente de Supabase en el navegador; el escáner extrae la URL del proyecto del bundle de cualquier modo.

¿Qué pasa si rotan mi clave anon o cambia mi clave publicable?

Vuelve a ejecutar el escaneo. El escáner extrae la clave del bundle actual en cada ejecución. La rotación solo invalida el reporte anterior, no el estado de las políticas de la base de datos.

¿El escáner comprueba el nuevo modelo de clave publicable de Supabase (<code>sb_publishable_*</code>)?

Sí. El detector reconoce tanto los JWT anon heredados como las claves más nuevas sb_publishable_* y las trata de forma idéntica — ambas están pensadas para ser públicas y ambas dejan a RLS como la única línea de defensa.

Próximos pasos

Ejecuta un escaneo gratuito de FixVibe contra tu URL de producción — la verificación baas.supabase-rls está habilitada en todos los planes, incluido el gratuito. Para una lectura más profunda sobre qué otras cosas pueden filtrarse desde un proyecto Supabase, consulta Clave de rol de servicio de Supabase expuesta en JavaScript y Lista de comprobación de seguridad de buckets de Supabase Storage. Para la vista general en todos los proveedores BaaS, lee Escáner de configuraciones erróneas de BaaS.

// escanea tu superficie de baas

Encuentra la tabla abierta antes que otra persona lo haga.

Introduce una URL de producción. FixVibe enumera los proveedores de BaaS con los que habla tu aplicación, identifica sus endpoints públicos y reporta lo que un cliente no autenticado puede leer o escribir. Gratis, sin instalación, sin tarjeta.

  • Plan gratuito — 3 escaneos al mes, sin tarjeta de registro.
  • Identificación pasiva de BaaS — no se requiere verificación de dominio.
  • Supabase, Firebase, Clerk, Auth0, Appwrite y más.
  • Prompts de corrección con IA en cada hallazgo — pégalos de vuelta en Cursor / Claude Code.
Escáner de RLS de Supabase: detecta tablas con seguridad a nivel de fila ausente o rota — Docs · FixVibe