Retour au blog
    Guide29 mai 20269 min de lecture

    RLS Supabase : l'erreur qui expose 1 app IA sur 10

    Le RLS Supabase protège vos tables — mal configuré, il laisse fuiter vos données. On explique les pièges les plus fréquents (policy vide, anon, SECURITY DEFINER) et comment les corriger.


    La faille CVE-2025-48757 a un coupable unique, et il revient dans la quasi-totalité des fuites d'apps générées par IA : un RLS Supabase mal configuré. Pas absent par malveillance — juste insuffisant, parce que générer une app qui marche et générer une app qui protège ses données sont deux exercices différents.

    Cet article est plus technique que les autres du dossier. Si vous (ou votre prestataire) avez les mains dans une app Supabase, voici exactement où ça casse et comment le corriger.

    RLS, en une minute

    Supabase expose votre base PostgreSQL directement au front-end via une API REST (PostgREST). La requête part du navigateur, avec une clé : la clé anon, qui est publique par conception (elle est dans le bundle JS).

    Ce qui protège vos tables n'est donc pas la clé — c'est le RLS (Row Level Security). Le RLS, ce sont des politiques (policies) SQL qui décident, ligne par ligne, qui a le droit de lire ou écrire. Sans RLS restrictif, la clé anon suffit à tout lire.

    Règle mentale : toute donnée accessible avec la clé anon doit être considérée comme publique. Tout le reste dépend de la qualité de vos policies.

    Les 4 erreurs qui exposent vos données

    1. Le RLS n'est pas activé du tout

    Le cas le plus brutal. Une table sans ENABLE ROW LEVEL SECURITY est lisible par n'importe qui avec la clé anon. C'est exactement ce qui a exposé des milliers de lignes dans la faille publique.

    -- À faire sur CHAQUE table contenant des données
    ALTER TABLE public.users ENABLE ROW LEVEL SECURITY;
    

    2. La policy "USING (true)" — la fausse sécurité

    Activer le RLS ne suffit pas si la policy n'autorise tout le monde. C'est le piège que le scanner natif de Lovable ne détecte pas : il voit qu'une policy existe, pas qu'elle ne filtre rien.

    -- DANGEREUX : autorise la lecture à tous, anon compris
    CREATE POLICY "read" ON public.users FOR SELECT USING (true);
    
    -- CORRECT : chaque utilisateur ne voit que ses lignes
    CREATE POLICY "read own" ON public.users
      FOR SELECT TO authenticated
      USING (auth.uid() = user_id);
    

    3. Le rôle anon oublié

    Beaucoup de policies sont écrites pour authenticated mais laissent des droits au rôle anon par héritage ou par défaut. Vérifiez les GRANT et ciblez explicitement les rôles. Dans la plupart des apps, anon ne devrait avoir accès à presque rien.

    4. Les fonctions SECURITY DEFINER avec search_path mutable

    Une fonction SECURITY DEFINER s'exécute avec les droits de son créateur — elle contourne le RLS. Si son search_path n'est pas fixé, elle devient une porte dérobée exploitable.

    -- Durcir chaque fonction SECURITY DEFINER
    ALTER FUNCTION public.ma_fonction() SET search_path = '';
    

    À cela s'ajoutent les RPC exécutables en anon et les buckets de stockage publics — autant de surfaces à vérifier.

    Comment corriger, étape par étape

    1. Activer le RLS sur chaque table contenant des données.
    2. Réécrire les policies permissives : remplacer USING (true) par des règles liées à auth.uid().
    3. Verrouiller le rôle anon : ne lui accorder que le strict nécessaire (souvent rien).
    4. Durcir les SECURITY DEFINER : set search_path = '' + droits d'exécution restreints.
    5. Tester en anon : interroger l'API REST avec la seule clé anon, table par table. Toute table qui renvoie des lignes sans login est exposée.

    L'étape 5 est la seule qui prouve quelque chose. C'est précisément ce que fait notre audit de sécurité, en lecture seule : un pré-scan instantané dans votre navigateur, ou un audit complet le jour même.

    Pourquoi c'est si fréquent sur les apps générées par IA

    Le générateur optimise pour le résultat visible : l'écran s'affiche, les données apparaissent. Que ces mêmes données soient aussi accessibles sans authentification ne se voit pas à l'usage — il faut explicitement aller interroger l'API en anon pour le constater. C'est un angle mort, pas une négligence : tant que personne ne regarde, tout semble fonctionner.

    C'est aussi pour ça qu'aucun "score de sécurité" automatique de surface ne remplace une vraie vérification. Une policy qui existe n'est pas une policy qui protège.

    FAQ — RLS Supabase

    Faut-il activer le RLS sur absolument toutes les tables ?

    Sur toutes les tables exposées via l'API (schéma public), oui. Une table sans RLS y est lisible avec la clé anon. Les tables internes non exposées peuvent suivre une autre logique, mais par défaut, RLS partout.

    La clé service_role est-elle concernée ?

    La clé service_role contourne le RLS — elle ne doit JAMAIS être exposée côté client, uniquement utilisée côté serveur (edge functions, backend). Si elle est dans votre bundle front, c'est une fuite critique à corriger immédiatement.

    Comment tester mon RLS sans risque ?

    En lecture seule : on interroge l'API REST avec la clé anon (publique) et on observe ce qui revient. Aucun écrit, aucune exploitation. C'est non destructif et c'est exactement ce que ferait un visiteur de votre site.

    Le RLS suffit-il à être conforme RGPD ?

    C'est une brique nécessaire mais pas suffisante. Le RGPD couvre aussi l'hébergement, la traçabilité des accès, la durée de conservation, le consentement. Le RLS verrouille l'accès aux données ; le reste relève de l'architecture et de la gouvernance.

    Ce qu'on en retient

    • La clé anon est publique : seul un RLS restrictif protège vos tables.
    • Quatre pièges : RLS désactivé, policy USING (true), rôle anon oublié, SECURITY DEFINER non durcie.
    • Une policy qui existe ≠ une policy qui protège — d'où l'angle mort des scores automatiques.
    • Le seul test fiable : interroger l'API en anon, table par table.

    Vous voulez savoir ce qui est exposé chez vous ? Lancez l'audit — en lecture seule, le jour même.

    Pour aller plus loin

    Vous vous reconnaissez ?

    Reservez un diagnostic opérationnel gratuit de 30 minutes.