// 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
A passive RLS scan looks at your Supabase project the way an anonymous visitor would. It uses only the publishable anon key — the same key your browser uses — and never authenticates as a user or touches service-role privileges. Anything it can see, an unauthenticated attacker on the internet can see.
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
SELECTanó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 rolanontenga cualquier privilegio. Las aplicaciones generadas por IA con frecuencia otorganUSAGEsobre el esquema ySELECTsobre cada tabla, lo que expone el mapa completo del esquema incluso cuando RLS niega las lecturas reales. - Read access does not establish write safety. A passive scan cannot prove that INSERT, UPDATE, or DELETE policies protect every user and tenant. Review write policies and test them with isolated fixtures under the intended roles.
- La clave de rol de servicio está en el bundle del navegador. Adyacente a RLS: si un escáner encuentra
SUPABASE_SERVICE_ROLE_KEYo cualquier JWT conrole: service_roleen 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
When you point Cursor, Claude Code, Lovable, or Bolt at Supabase, the same four broken-RLS patterns come up again and again. Each one passes type-check, compiles, and ships:
Forma 1: RLS nunca habilitado
The most common failure mode. The migration creates the table but the developer (or the AI tool) forgets ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST happily serves the entire table to anyone with the anon key. Fix: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;, then add a policy per command. Enabling RLS with no policies denies all access through the API; the policies decide who gets in.
ALTER TABLE public.[name] ENABLE 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.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);Forma 3: La política compara la columna consigo misma
Un artefacto de copiar y pegar. El desarrollador escribe USING (user_id = user_id) — que siempre es verdadero — en lugar de USING (auth.uid() = user_id). La verificación de tipos pasa; la política permite todas las filas. Solución: compara siempre una columna con una llamada a función (auth.uid(), auth.jwt()->>'org_id', etc.), nunca consigo misma ni con una constante.
Shape 4: Overly permissive write policies
With RLS enabled, a role with no applicable policy is denied by default. A SELECT policy does not itself allow INSERT. Write exposure can arise from a permissive applicable INSERT or ALL policy, disabled RLS, or a role that bypasses RLS. Fix: inspect table privileges and every applicable policy, use ownership-scoped WITH CHECK conditions, and test writes with separate user identities.
Cómo funciona el escáner de RLS de Supabase de FixVibe
FixVibe's Supabase check works from the deployed app, in three steps:
- Find the project. FixVibe loads the deployed app and reads the Supabase project URL and public key from the same configuration your browser receives. No guessing, no brute force.
- See what is exposed. FixVibe checks which tables the anonymous role can see, without reading row data at this step.
- Stage 3 — inspect anonymous read access. The passive check reports observable access to discovered resources. It does not create, update, or delete database rows, and it cannot establish that all write policies or cross-user boundaries are correct.
Each finding ships with the exact request URL, response status, response shape (header-only), and the table name. FixVibe now labels whether the fix belongs in code/config, a provider console, DNS, secret rotation, or manual review so the next step matches who can actually apply it.
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:
- Audita cada tabla. Ejecuta
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';en el editor SQL de Supabase. Cualquier fila conrowsecurity = falsees un problema. - Enable RLS on every public table. Default to
ENABLE ROW LEVEL SECURITYon every table created — make it a migration template. - 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 aauth.uid()o a una columna de org-id desdeauth.jwt(). - 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. - Re-scan. After applying the fix, re-run a FixVibe scan against the same URL. The Supabase RLS finding should clear.
-- 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?
The passive RLS check is read-only. It examines observable anonymous read access and reports limited evidence. It does not perform database write operations or prove that every user and tenant is correctly isolated.
¿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 (sb_publishable_*)?
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
Run a free FixVibe scan against your production URL — the Supabase RLS check runs on every plan including the free tier. For a deeper read on what else can leak from a Supabase project, see Supabase service role key exposed in JavaScript and Supabase storage bucket security checklist. For the umbrella view across all BaaS providers, read BaaS misconfiguration scanner.
