“Un classeur dans un hall public avec un panneau « réservé au personnel », et pas de serrure.”
Le client n'est pas de votre côté
Les outils IA modernes adorent un schéma où le navigateur parle directement à une base de données hébergée comme Supabase ou Firebase. C'est rapide à construire et magnifique en démo. Le problème est le modèle de sécurité : le navigateur embarque une clé vers la base de données, et le navigateur est entièrement sous le contrôle de l'utilisateur. N'importe qui peut ouvrir les outils développeur, copier cette clé et interroger la base directement, en contournant votre app, vos écrans et toute vérification qui vivait dans le code front-end.
browser (any user) your UI says database ┌───────────────┐ 'you may see ┌─────────────┐ │ anon key ────┼─────────── your row' ────X ───▶│ every row │ │ devtools open │ (front-end only) │ for everyone │ └───────────────┘ ◀─────── returns ALL rows ────└─────────────┘
Ainsi, une règle appliquée dans votre composant React, « n'afficher que les commandes de l'utilisateur courant », n'est pas un contrôle de sécurité. C'est une préférence d'affichage. La base de données livrera volontiers les mêmes données à une requête brute qui ne charge jamais votre composant. La décision d'accès doit vivre là où l'utilisateur ne peut pas l'atteindre : dans la base de données elle-même.
Ce qu'est réellement Row-Level Security
Row-Level Security (RLS) déplace la décision d'accès dans la base de données, ligne par ligne. Une fois activée, la base refuse toute requête par défaut, et vous écrivez des politiques qui disent exactement quelles lignes un utilisateur donné peut voir ou modifier. La politique classique : un utilisateur ne peut lire une ligne que si la colonne propriétaire de la ligne est égale à son id authentifié. Désormais, la requête directe qui contournait votre UI ne récupère plus rien, parce que c'est la base de données, et non le navigateur, qui décide.
-- Turn RLS on, THEN add a policy. RLS on with no policy = deny everything. alter table orders enable row level security; create policy "owners read their own orders" on orders for select using ( (select auth.uid()) = user_id );
C'est toute l'idée : refuser par défaut, puis accorder étroitement. C'est le même principe que le moindre privilège partout ailleurs : partir de « personne ne peut toucher à rien » et ouvrir la plus petite porte qui fait fonctionner la fonctionnalité.
Les erreurs qui fuient encore avec RLS « activée »
Activer RLS est nécessaire mais pas suffisant. Voici comment les apps fuient malgré tout, et un outil IA produira allègrement chacune de ces erreurs :
- RLS activée sur certaines tables, oubliée sur d'autres : la table ajoutée la semaine dernière n'a aucune règle et est grande ouverte.
- Une politique si permissive qu'elle accorde tout (using (true)), ce qui revient à aucune politique avec des étapes en plus.
- Livrer la clé service-role / admin au client : cette clé contourne entièrement la RLS, donc toutes les politiques deviennent caduques.
- Vérifier la propriété contre une valeur envoyée par le client, au lieu de l'id d'authentification vérifié côté serveur : le client envoie simplement une autre valeur.
Comment vérifier réellement
Ne présumez pas que c'est fait parce que l'interrupteur est activé. Vérifiez-le comme le ferait un attaquant :
- Listez chaque table. Confirmez que la RLS est activée ET qu'une vraie politique bien délimitée existe sur chacune, pas seulement activée.
- Interrogez en tant qu'utilisateur anonyme avec la clé publique et confirmez que vous n'obtenez rien d'indu.
- Connectez-vous avec un second utilisateur et confirmez que vous ne pouvez ni lire ni écrire les lignes du premier.
- Cherchez la clé service-role/admin dans votre bundle client et votre config d'environnement. Elle ne devrait pas s'y trouver.
C'est quinze minutes de travail et c'est la vérification de sécurité au meilleur rendement que vous puissiez exécuter sur une app construite par IA. La CVE Lovable était, au fond, une version à l'échelle de l'industrie de l'avoir sautée.
En une ligne chacun
- Les schémas navigateur-vers-base livrent une clé que l'utilisateur contrôle ; toute vérification côté front-end est de la décoration, pas de la sécurité.
- Row-Level Security déplace la décision dans la base de données, ligne par ligne : refuser par défaut, puis accorder étroitement.
- RLS « activée » fuit encore via des tables oubliées, des politiques trop permissives, une clé service-role côté client, ou la confiance dans des ids envoyés par le client.
- Vérifiez comme un attaquant : chaque table a une vraie politique, l'anonyme n'obtient rien, un second utilisateur est bloqué, aucune clé admin dans le client.
Où aller ensuite