// 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
Un scan RLS passif s'exécute contre l'endpoint PostgREST que Supabase expose à https://[project].supabase.co/rest/v1/. Il utilise uniquement la clé anon publiable — la même que celle utilisée par votre navigateur — et sonde les métadonnées de liste de tables, les lectures anonymes et les écritures anonymes. Il ne s'authentifie jamais en tant qu'utilisateur et ne touche jamais aux privilèges de rôle de service. Tout ce qu'il peut faire, un attaquant non authentifié sur Internet peut le faire.
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. - Le rôle anonyme peut insérer. Un
POSTde sondage avec une supposition sur la forme des colonnes réussit si RLS n'a pas de politiqueINSERTqui le refuse — même siSELECTest verrouillé. - 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
Lorsque vous pointez Cursor, Claude Code, Lovable ou Bolt sur Supabase, les mêmes quatre motifs de RLS défaillante apparaissent dans des milliers d'applications. Chacun passe la vérification de types, compile et est livré :
Forme 1 : RLS jamais activé
Le mode d'échec le plus courant. La migration crée la table mais le développeur (ou l'outil IA) oublie ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST sert joyeusement la table entière à quiconque possède la clé anon. Correction : ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE n'est pas optionnel — sans lui, le propriétaire de la table (et tout rôle ayant la propriété de la table) contourne RLS.
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE 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
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.
Forme 4 : politique sur SELECT mais pas sur INSERT/UPDATE
Le développeur verrouille les lectures mais oublie les écritures. Les politiques RLS sont par commande. FOR SELECT ne protège que les lectures ; un client anonyme peut toujours INSERT si aucune politique ne le refuse. Correction : rédigez une politique par commande, ou utilisez FOR ALL avec des clauses USING et WITH CHECK explicites.
Comment fonctionne le scanner RLS Supabase de FixVibe
Le check baas.supabase-rls s'exécute en trois étapes, chacune avec des niveaux de confiance explicites :
- Étape 1 — identification. Le scanner parcourt l'application déployée, analyse son bundle JavaScript et extrait l'URL du projet Supabase et la clé anon de la configuration runtime. Pas de devinette DNS, pas de force brute — il lit ce que le navigateur lit.
- Étape 2 — découverte du schéma. Un unique
GET /rest/v1/avec la clé anon retourne le schéma OpenAPI de chaque table que le rôle anon peut voir. Le scanner enregistre les noms de tables mais ne lit pas les données de lignes à ce stade. - Étape 3 — sondages de lecture et d'écriture. Pour chaque table découverte, le scanner émet un
SELECTanonyme aveclimit=1. Si des lignes sont retournées, RLS est permissif. Le scanner s'arrête là — il n'énumère pas les lignes, ne pagine pas, ne modifie pas les données. Les sondages INSERT sont conditionnés par la vérification de la propriété de domaine et un opt-in explicite ; ils ne se déclenchent jamais contre des cibles non vérifiées.
Chaque résultat est livré avec l'URL exacte de la requête, le statut de la réponse, la forme de la réponse (en-tête uniquement) et le nom de la table. Le prompt de correction IA au bas du résultat est un bloc SQL prêt à coller que vous exécutez dans l'éditeur SQL Supabase.
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. - Activez RLS sur chaque table publique. Par défaut, appliquez
ENABLE ROW LEVEL SECURITYetFORCE ROW LEVEL SECURITYsur chaque table créée — faites-en un modèle de migration. - 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. - Rescanner. Après application de la correction, relancez un scan FixVibe contre la même URL. Le résultat
baas.supabase-rlsdevrait disparaître.
-- 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 ?
Non. Les scans passifs émettent au plus un SELECT ... limit=1 par table découverte pour confirmer si RLS autorise les lectures anonymes. Le scanner enregistre la forme de la réponse, pas le contenu des lignes. Les sondages INSERT, UPDATE et DELETE sont conditionnés par la vérification de la propriété de domaine et ne s'exécutent jamais contre des cibles non vérifiées.
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 (<code>sb_publishable_*</code>) ?
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
Lancez un scan FixVibe gratuit contre votre URL de production — le check baas.supabase-rls est activé sur tous les plans, y compris le gratuit. Pour une lecture plus approfondie sur ce qui peut fuir d'autre depuis un projet Supabase, voir Clé de rôle de service Supabase exposée en JavaScript et Liste de contrôle de sécurité des buckets Supabase Storage. Pour la vue d'ensemble sur tous les fournisseurs BaaS, lisez Scanner de mauvaises configurations BaaS.
