// docs / baas security / supabase rls scanner
Scanner RLS Supabase : détecter les tables avec sécurité au niveau des lignes absente ou défaillante
La sécurité au niveau des lignes (RLS) est la seule chose qui se tient entre les données de vos clients et Internet lorsque vous mettez en ligne une application adossée à Supabase. Les outils de codage IA génèrent du code en forme de RLS qui compile, est livré et fuit silencieusement des données — tables créées sans RLS activé, politiques qui lisent mais ne restreignent jamais, prédicats qui comparent une colonne à elle-même. Cet article montre ce qu'un scanner RLS Supabase peut prouver depuis l'extérieur, les quatre formes de RLS défaillante qui apparaissent dans les applications vibe-codées et comment scanner votre propre déploiement en moins d'une minute.
Ce qu'un scan RLS externe peut prouver
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.
Depuis l'extérieur de la base de données, un scanner peut confirmer ce qui suit avec un haut niveau de confiance :
- RLS est désactivé sur une table. PostgREST retourne des lignes pour un
SELECTanonyme lorsque RLS est désactivé ou lorsqu'une politique le permet. Les deux cas constituent un résultat. - Le rôle anonyme peut lister les tables. Un
GET /rest/v1/avec la clé anon retourne le schéma OpenAPI pour chaque table sur laquelle le rôleanona un quelconque privilège. Les applications générées par IA accordent fréquemmentUSAGEsur le schéma etSELECTsur chaque table, ce qui expose la carte complète du schéma même lorsque RLS refuse les lectures réelles. - 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 clé de rôle de service est dans le bundle du navigateur. Adjacent à RLS : si un scanner trouve
SUPABASE_SERVICE_ROLE_KEYou tout JWT avecrole: service_roledans le bundle JavaScript, RLS est sans objet — le détenteur de cette clé contourne toute politique.
Ce qu'un scan externe ne peut pas prouver
Soyez honnête sur les limites du scanner. Un scan RLS externe ne peut pas lire votre table pg_policies, vos fichiers de migration ni le prédicat exact d'une politique. Il déduit du comportement en boîte noire, ce qui signifie qu'il signalera parfois un résultat qui s'avère être des données publiques intentionnelles (une table de newsletter marketing, un catalogue produit public). Le rapport FixVibe les marque comme confiance moyenne lorsque le scanner ne peut pas désambiguïser l'intention — examinez le nom de la table et décidez.
Les quatre formes de RLS défaillante que produisent les outils 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:
Forme 1 : RLS jamais activé
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;Forme 2 : RLS activé, pas de politiques
Un échec plus subtil. RLS est activé mais aucune politique n'est écrite. Le défaut dans PostgreSQL est refuser, donc les utilisateurs authentifiés ne voient rien — et le développeur ajoute USING (true) pour faire fonctionner l'application, ce qui permet à tout le monde de tout lire. Correction : écrivez une politique qui limite par auth.uid() : CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); et une politique INSERT/UPDATE/DELETE correspondante.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);Forme 3 : la politique compare la colonne à elle-même
Un artefact de copier-coller. Le développeur écrit USING (user_id = user_id) — qui est toujours vrai — au lieu de USING (auth.uid() = user_id). La vérification de types passe ; la politique autorise chaque ligne. Correction : comparez toujours une colonne à un appel de fonction (auth.uid(), auth.jwt()->>'org_id', etc.), jamais à elle-même ou à une 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.
Comment fonctionne le scanner RLS 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.
Que faire quand le scanner trouve quelque chose
Chaque résultat RLS est une urgence en production. Les endpoints PostgREST publics sont scannés par des attaquants en quelques minutes. La séquence de remédiation est mécanique :
- Auditez chaque table. Exécutez
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';dans l'éditeur SQL Supabase. Toute ligne avecrowsecurity = falseest un problème. - Enable RLS on every public table. Default to
ENABLE ROW LEVEL SECURITYon every table created — make it a migration template. - Rédigez des politiques commande par commande. N'utilisez pas
FOR ALL USING (true). Rédigez des politiques explicites pour SELECT, INSERT, UPDATE, DELETE — chacune limitée àauth.uid()ou à une colonne d'org-id depuisauth.jwt(). - Vérifiez avec un second compte. Inscrivez-vous en tant qu'autre utilisateur, tentez de lire les enregistrements d'un autre utilisateur via l'API REST directement. Si la réponse est
200, la politique est cassée. - 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;Comment cela se compare aux autres scanners
La plupart des outils DAST génériques (Burp Suite, OWASP ZAP, Nessus) ne savent pas ce qu'est PostgREST. Ils parcourront votre application, ignoreront le chemin /rest/v1/ et rapporteront sur les pages HTML qu'ils comprennent. Snyk et Semgrep sont des outils d'analyse statique — ils trouvent des fichiers de migration dans votre repo avec des appels RLS manquants, mais ne peuvent pas prouver que la base de données déployée est mal configurée. FixVibe occupe ce vide : passif, conscient du BaaS, axé sur ce qu'un attaquant non authentifié peut prouver depuis l'URL publique.
Questions fréquentes
Le scanner lira-t-il ou modifiera-t-il mes données ?
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.
Cela fonctionne-t-il si mon projet Supabase est en pause ou sur un domaine personnalisé ?
Les projets en pause renvoient 503 à chaque requête — le scanner signale le projet comme inaccessible. Les domaines personnalisés fonctionnent tant que l'application déployée charge encore le SDK client Supabase dans le navigateur ; le scanner extrait l'URL du projet du bundle dans tous les cas.
Que se passe-t-il si ma clé anon est rotée ou si ma clé publiable change ?
Relancez le scan. Le scanner extrait à nouveau la clé du bundle actuel à chaque exécution. La rotation invalide seulement le rapport précédent, pas l'état des politiques de la base de données.
Le scanner vérifie-t-il le nouveau modèle de clé publiable Supabase (sb_publishable_*) ?
Oui. Le détecteur reconnaît à la fois les JWT anon historiques et les nouvelles clés sb_publishable_* et les traite à l'identique — les deux sont destinés à être publics et tous deux laissent RLS comme seule ligne de défense.
Étapes suivantes
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.
