Supabase Row Level Security (RLS) : verrouillez vos tables
Supabase expose vos tables Postgres directement au navigateur via sa Data API, authentifiée avec l’anon key publique (publishable key). Les policies de Row Level Security (RLS) décident quelles lignes chaque utilisateur peut lire ou modifier. Une table du schéma public sans RLS est lisible — et souvent modifiable — par quiconque ouvre votre site.
Ce que c’est
Chaque projet Supabase est livré avec une anon key (ou sb_publishable_) destinée à vivre dans votre frontend. Elle identifie votre projet, pas votre utilisateur : n’importe qui peut la copier depuis votre bundle JavaScript et envoyer lui-même des requêtes à https://<project>.supabase.co/rest/v1/, en contournant complètement votre interface.
Row Level Security est une fonctionnalité de Postgres qui attache des policies à une table : des conditions SQL vérifiées pour chaque ligne, à chaque SELECT, INSERT, UPDATE et DELETE. Avec le RLS activé et aucune policy, rien n’est accessible via l’API. Chaque policy que vous ajoutez ouvre exactement une porte — par exemple « les utilisateurs connectés peuvent lire les lignes dont le user_id correspond à leur propre id ».
Pourquoi ça compte
Une policy manquante ou trop large n’est pas un bug mineur, c’est une fuite de données complète : e-mails, profils, commandes, messages privés — tout ce que contient la table — peuvent être aspirés avec une seule commande curl. Les outils de code IA créent souvent des tables via des migrations SQL, où le RLS est désactivé par défaut, et génèrent des policies provisoires permissives comme using (true) juste pour que l’app fonctionne.
La clé service_role (sb_secret_) contourne entièrement le RLS. Si elle se retrouve dans le code côté client, toutes les policies que vous avez écrites deviennent inutiles : quiconque la détient a un accès complet en lecture et en écriture à votre base de données.
Comment corriger
Exécutez ceci dans le SQL Editor de Supabase pour lister chaque table du schéma public exposé dont le RLS est encore désactivé. Le Security Advisor du dashboard signale le même problème sous le nom « RLS disabled in public ».
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Activez le RLS sur chaque table, puis ajoutez une policy par opération. Limitez les policies au rôle authenticated et comparez la colonne propriétaire de la ligne à auth.uid(). Les insertions et mises à jour ont besoin de with check pour que les utilisateurs ne puissent pas écrire des lignes appartenant à quelqu’un d’autre.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Remplacez les policies provisoires comme using (true) par de vraies conditions. Ne fondez pas l’autorisation sur user_metadata — les utilisateurs peuvent le modifier eux-mêmes ; utilisez plutôt app_metadata ou une table de rôles. Les vues contournent le RLS sauf si elles sont créées avec security_invoker = true, et les buckets de stockage ont besoin de leurs propres policies sur storage.objects.
Interrogez votre API exactement comme le ferait un attaquant : avec seulement la clé publique et sans session utilisateur. Si cela renvoie des lignes qui ne devraient pas être publiques, vos policies sont trop ouvertes.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"N’utilisez la clé service_role (sb_secret_) que dans du code serveur, comme des routes API ou des Edge Functions, jamais derrière un préfixe NEXT_PUBLIC_ ou VITE_. Si elle a déjà été livrée au navigateur, faites-la tourner depuis le dashboard.
FAQ
Est-ce sans danger que mon anon key Supabase soit visible dans le navigateur ?
Oui — l’anon key (publishable key) est conçue pour être publique, mais seulement si chaque table exposée a le RLS activé avec des policies correctes. La clé elle-même n’accorde rien que vos policies n’autorisent pas.
J’ai activé le RLS et mon app renvoie maintenant des résultats vides. Est-elle cassée ?
Non, c’est le RLS qui fonctionne comme prévu : sans policies, chaque ligne est refusée. Ajoutez des policies pour les opérations dont votre app a besoin, limitées aux bons utilisateurs, plutôt que de désactiver à nouveau le RLS.
AppSafe teste-t-il mes policies RLS ?
AppSafe ne se connecte pas à votre base de données, il ne peut donc pas évaluer vos policies — utilisez les vérifications SQL et curl ci-dessus. En revanche, il analyse votre JavaScript à la recherche de clés Supabase. Si l’une d’elles est signalée, décodez la partie centrale du JWT et vérifiez le claim role : anon est attendu dans le navigateur, service_role est une fuite critique.
Votre app est-elle concernée ?
AppSafe détecte ce problème et des dizaines d’autres en un seul scan gratuit.
Scanner mon app gratuitementGuides liés
Firebase Security Rules : ne laissez plus votre base de données ouverte
Votre config Firebase est publique par conception. Ce sont vos règles qui décident qui voit les données.
API keys exposées dans le JavaScript côté client
Tout ce qui est dans votre bundle est public. Traitez-le comme tel.
Sauvegardes de base de données exposées : que se passe-t-il quand /backup.sql est accessible
Un dump .sql accessible, c'est la remise complète de toute votre base de données.