Sécuriser une application Lovable et Supabase avant d'ouvrir aux clients : la check-list
Votre application Lovable fonctionne en démo. Avant d'y faire entrer de vrais clients, huit points à vérifier vous-même dans le dashboard Supabase et le navigateur : RLS, clés, fonctions, stockage, authentification, sauvegardes, limites de débit.
Mustapha Hamadi, Développeur Full-Stack14 min de lecture
Sommaire
Le 27 février 2026, The Register rapportait qu'une application construite avec Lovable, mise en avant sur la vitrine de la plateforme, laissait n'importe quel visiteur non connecté lire 18 697 enregistrements d'utilisateurs, dont 4 538 comptes d'étudiants. Le chercheur Taimur Khan y avait relevé seize failles, dont six critiques. La plus grave tenait en une ligne : une fonction de contrôle d'accès à la logique inversée, qui bloquait les utilisateurs connectés et laissait passer les autres. L'application marchait parfaitement en démo.
Chez Raicode, nous avons repris et mis en production une douzaine d'applications générées par IA (Lovable, Bolt, v0, Cursor), dont une application RH et une messagerie chiffrée interne d'entreprise où il a fallu corriger de nombreuses failles. Ce qu'on retrouve souvent : une interface soignée, des parcours qui fonctionnent, et une sécurité qui repose sur des réglages Supabase que personne n'a regardés.
Cette check-list se fait sans savoir coder, avec le dashboard Supabase et un navigateur. Pour chaque point : vérifier, mesurer le risque, corriger.
Pourquoi une application Lovable est exposée par construction
Une application Lovable créée avant le 13 mai 2026 est une application Vite et React qui parle directement à Supabase depuis le navigateur, sans serveur intermédiaire. Les projets plus récents tournent sur TanStack Start, avec un rendu côté serveur ; s'ils appellent eux aussi Supabase depuis le navigateur, ce qui suit vaut à l'identique. Dans ce modèle, le code envoyé à chaque visiteur contient l'adresse de votre base et une clé publique, réutilisables par n'importe qui hors de votre interface. Ce modèle tient à une condition : que la base refuse elle-même ce qui n'est pas autorisé, grâce au Row Level Security (RLS), les règles d'accès écrites dans Postgres. Quand elles manquent, la clé publique ouvre tout.
Ce n'est pas théorique. En mars 2025, Matt Palmer a analysé 1 645 applications de la vitrine Lovable et trouvé 303 points d'accès vulnérables répartis sur 170 projets, soit environ 10,3 % : des tables lisibles ou modifiables avec la seule clé publique. Parmi les données exposées : noms, téléphones, clés d'API, informations de paiement. La faille est enregistrée sous la référence CVE-2025-48757, notée 9,3 sur 10 ; Lovable conteste l'attribution, au motif que chaque client est responsable de la protection des données de son application. Pour vous, la conclusion est la même : c'est à vous de vérifier.
1. Le RLS désactivé ou trop permissif
Vérifier. Dans le dashboard Supabase, ouvrez Advisors, puis Security Advisor, et lancez l'analyse. Deux alertes comptent ici : rls_disabled_in_public (niveau ERROR, « Table publicly accessible »), qui signale une table exposée sans aucune règle, et permissive_rls_policy, qui signale une règle trop large. Pour une vue d'ensemble, collez cette requête dans le SQL Editor :
select tablename, rowsecurity
from pg_tables
where schemaname = 'public';
Chaque false est une table ouverte à la clé publique.
Activer le RLS ne suffit pas : il faut lire les règles. Ce qu'on retrouve souvent dans les applications générées, c'est une règle qui autorise tout le monde, écrite pour faire disparaître une erreur pendant le développement :
create policy "Enable read access for all users"
on public.factures for select
using (true);
Le test le plus parlant se fait de l'extérieur. Ouvrez votre application, F12, onglet Réseau (Network), filtre supabase.co : l'en-tête apikey d'une requête vers /rest/v1/ contient votre clé publique. Dans un terminal, sans être connecté :
curl "https://VOTRE-PROJET.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: VOTRE_CLE_PUBLIQUE"
Si vous recevez les profils de vos utilisateurs, n'importe qui peut en faire autant.
Le risque. Lecture de toutes les données de tous les clients, et souvent écriture : modifier un prix, s'attribuer un rôle d'administrateur, supprimer des lignes.
Corriger. Activez le RLS sur chaque table du schéma public, puis écrivez une règle par opération, en nommant le rôle concerné. La documentation de Supabase recommande de toujours préciser le rôle avec la clause to et d'écrire (select auth.uid()) plutôt que auth.uid(), pour que la fonction soit évaluée une fois par requête et non une fois par ligne :
alter table public.factures enable row level security;
create policy "Lecture de ses propres factures"
on public.factures for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Création de ses propres factures"
on public.factures for insert
to authenticated
with check ((select auth.uid()) = user_id);
Une table avec RLS mais sans règle ne renvoie plus rien par l'API : c'est l'état sûr, et votre application vous dira vite ce qui manque. Pour une application B2B où plusieurs utilisateurs partagent les données d'une même entreprise, la règle passe par une table d'appartenance (utilisateur, organisation) plutôt que par user_id.
Deux pièges reviennent. Ne fondez jamais une règle sur user_metadata, que l'utilisateur peut modifier lui-même : un rôle « admin » stocké là, chacun peut se l'attribuer. Et une vue mal déclarée contourne le RLS des tables qu'elle lit (alerte security_definer_view).
2. La clé service_role dans le bundle front
Supabase fournit deux familles de clés, visibles dans Settings, puis API Keys. La clé publique (anon, ou sb_publishable_... dans le nouveau format) est faite pour le navigateur. La clé secrète (service_role, ou sb_secret_...) contourne toutes les règles RLS : elle n'a sa place que sur un serveur.
Vérifier. Ouvrez votre site en production, F12, onglet Sources, puis la recherche dans tous les fichiers (Cmd+Option+F sur Mac, Ctrl+Shift+F sous Windows). Cherchez sb_secret_, puis service_role, puis SERVICE_ROLE.
Attention : une clé service_role au format historique est un jeton JWT qui commence par eyJ, et le mot « service_role » y est encodé, donc invisible à la recherche. Cherchez eyJ et, pour chaque jeton trouvé, décodez sa partie centrale dans la console du navigateur :
JSON.parse(atob("COLLEZ_LE_JETON".split(".")[1].replace(/-/g, "+").replace(/_/g, "/")))
Si le résultat contient "role": "service_role", la clé secrète est publique.
Le risque. Total. Quiconque ouvre le code source de votre page peut lire, modifier et supprimer toutes les données, quelles que soient vos règles RLS.
Corriger. Dans l'ordre, et vite :
- créez une nouvelle clé secrète dans Settings, puis API Keys ;
- retirez la clé du code front et déplacez le traitement qui en avait besoin dans une Edge Function ;
- remplacez l'ancienne clé partout où un serveur l'utilise ;
- désactivez l'ancienne clé
service_role, ou supprimez l'ancienne clésb_secret_.
La clé a pu être copiée par un robot : changer le code sans changer la clé ne sert à rien. Supabase annonce d'ailleurs l'abandon des clés anon et service_role d'ici fin 2026 : c'est l'occasion de passer au nouveau format.
3. Les clés d'API tierces exposées
Même mécanisme, autres fournisseurs : OpenAI, Stripe, Resend, Google Maps. Avec Vite, la règle est simple : toute variable préfixée VITE_ finit dans le code envoyé au navigateur. Une VITE_OPENAI_API_KEY est une clé publiée.
Vérifier. Même recherche dans l'onglet Sources : sk- (OpenAI), sk_live_ (clé secrète Stripe), re_ (Resend), AIza (Google). Une clé Stripe pk_live_ est publique par nature, ce n'est pas une alerte. Ouvrez aussi le fichier .env du dépôt et listez toutes les variables VITE_ : chacune doit pouvoir être lue par un inconnu sans conséquence.
Vérifiez enfin l'historique. Lovable a reconnu qu'entre le 3 février et le 20 avril 2026, l'historique des conversations et le code source des projets publics étaient accessibles à tout utilisateur qui avait le lien du projet ; les projets privés n'étaient pas concernés. Si votre projet était public sur cette période et qu'une clé a transité par le chat ou le code, considérez-la comme compromise. Même chose pour une clé déjà poussée dans un dépôt Git, même supprimée depuis.
Le risque. Une facture d'IA qui s'emballe, des e-mails envoyés en votre nom, un accès à vos paiements.
Corriger. Révoquez et régénérez la clé chez le fournisseur. Stockez la nouvelle dans les secrets des Edge Functions Supabase, et faites passer l'appel par une fonction qui vérifie l'utilisateur avant d'appeler le service. Chez le fournisseur, posez un plafond de dépense quand l'option existe.
4. Les fonctions RPC et les Edge Functions sans contrôle d'accès
La logique sensible vit souvent dans des fonctions Postgres appelées par supabase.rpc(...), ou dans des Edge Functions. C'est le bon réflexe, si ces fonctions vérifient qui les appelle.
Vérifier les fonctions RPC. Par défaut, Supabase indique que n'importe quel rôle peut exécuter une fonction de base de données, y compris anon, c'est-à-dire un visiteur non connecté. Le danger vient des fonctions déclarées security definer : elles s'exécutent avec les droits de leur créateur, qui contourne le RLS. Dans le SQL Editor :
select proname, prosecdef
from pg_proc
where pronamespace = 'public'::regnamespace;
Chaque ligne à true dans prosecdef mérite une lecture attentive. Dans l'onglet Réseau, les appels apparaissent sous /rest/v1/rpc/ suivi du nom de la fonction : rejouez-les sans être connecté avec la commande curl du point 1. Le Security Advisor signale aussi function_search_path_mutable, une fonction dont le chemin de recherche n'est pas fixé.
Corriger. Préférez security invoker, qui est le mode par défaut et applique le RLS de l'appelant. Si security definer est nécessaire, fixez le search_path, vérifiez l'appelant dans le corps de la fonction, et retirez le droit d'exécution aux visiteurs anonymes :
revoke execute on function public.valider_commande from public, anon;
grant execute on function public.valider_commande to authenticated;
Vérifier les Edge Functions. Dans le dépôt, ouvrez supabase/config.toml : chaque fonction déclarée avec verify_jwt = false accepte des appels sans jeton. Ce réglage n'est légitime que pour un webhook dont la signature est vérifiée dans le code, comme ceux de Stripe. Pour les autres, verify_jwt = true est la valeur par défaut, mais elle valide un jeton, pas une permission : la fonction doit encore lire l'utilisateur et vérifier qu'il a le droit de faire ce qu'il demande. Erreur fréquente : une fonction qui fait confiance à l'identifiant de client reçu dans la requête.
Le risque. Ce sont les failles décrites par The Register : envoyer des e-mails en masse, supprimer des comptes, modifier des notes, sans être connecté.
5. Le stockage public
Vérifier. Ouvrez Storage dans le dashboard. Chaque bucket est public ou privé. Pour un bucket public, Supabase précise que quiconque possède l'URL d'un fichier peut y accéder, sans aucun contrôle. Et ces URL se devinent souvent, quand elles contiennent l'identifiant de l'utilisateur ou le nom du document.
Le risque. Pour des photos de produits, aucun. Pour des pièces d'identité, des contrats, des fiches de paie ou des pièces jointes de messagerie, c'est une fuite de données personnelles au sens du RGPD.
Corriger. Passez en privé tout bucket qui contient autre chose que des fichiers destinés au public, servez les fichiers par des URL signées à durée limitée, et écrivez des règles sur storage.objects. L'exemple de la documentation range chaque fichier dans un dossier au nom de l'utilisateur :
create policy "Dépôt dans son propre dossier"
on storage.objects for insert
to authenticated
with check (
bucket_id = 'documents'
and (storage.foldername(name))[1] = (select auth.jwt()->>'sub')
);
Faites de même pour select, update et delete. Sur un bucket public, dépôt et suppression restent soumis aux règles : vérifiez qu'un visiteur ne peut pas remplacer vos fichiers.
6. L'authentification : confirmation, réinitialisation, liens magiques
Vérifier la confirmation d'e-mail. Dans Authentication, puis Sign In / Providers, section Email, le réglage Confirm email doit être actif. Il l'est par défaut sur les projets hébergés, mais on le retrouve souvent coupé pour tester plus vite. Désactivé, n'importe qui peut créer un compte avec l'adresse de quelqu'un d'autre, ce qui devient grave dès que votre logique donne des droits selon le domaine de l'e-mail.
Vérifier les redirections. Dans Authentication, puis URL Configuration, la Site URL vaut http://localhost:3000 par défaut. Supabase rappelle que ce réglage est critique pour les confirmations d'e-mail et les réinitialisations de mot de passe : laissé ainsi, vos clients reçoivent des liens qui ne marchent pas. Dans Redirect URLs, les jokers comme ** sont pratiques pour les URL de préversion, mais la documentation recommande l'adresse exacte en production.
Tester la réinitialisation. Demandez un nouveau mot de passe pour une adresse qui existe, puis pour une qui n'existe pas : le message affiché doit être identique, sinon votre formulaire révèle qui est client. Suivez le lien reçu jusqu'au bout et vérifiez qu'il mène à une page de changement de mot de passe sur votre domaine.
Vérifier les liens magiques. Ils sont à usage unique et expirent selon le réglage Email OTP expiration, une heure par défaut, qui s'applique aussi aux liens de confirmation et de réinitialisation. Ne l'allongez pas. Si votre application est sur invitation, sachez que signInWithOtp crée un compte quand l'adresse est inconnue : il faut passer shouldCreateUser: false, ou désactiver Allow new users to sign up.
Le risque. Comptes usurpés, prise de contrôle par un lien mal redirigé, inscriptions ouvertes sur un outil censé être fermé.
Corriger. Activez aussi une longueur minimale de mot de passe dans les réglages d'authentification (la documentation déconseille moins de 8 caractères) et, sur le plan Pro et au-delà, la protection contre les mots de passe divulgués.
7. L'absence de sauvegarde
Vérifier. Ouvrez Database, puis Backups. Supabase sauvegarde chaque jour les projets Pro, Team et Enterprise, avec 7 jours d'historique sur le plan Pro. Rien n'est prévu pour le plan gratuit, sur lequel beaucoup d'applications Lovable sont encore au moment du lancement.
Le risque. Une migration ratée, un prompt qui fait réécrire une table, une suppression malveillante : sans sauvegarde, tout est perdu.
Corriger. Passez au plan Pro avant d'accueillir des clients payants. Si perdre une journée de données est inacceptable, activez le Point-in-Time Recovery, une option payante qui permet de restaurer à la seconde près. Deux angles morts : les sauvegardes n'incluent pas les fichiers du Storage, seulement leurs métadonnées, et une sauvegarde qu'on n'a jamais restaurée reste une hypothèse. Faites un essai de restauration sur un projet de test, et gardez un export régulier hors de Supabase avec la commande supabase db dump.
8. Les limites de débit
Vérifier. Ouvrez Authentication, puis Rate Limits. Avec le fournisseur d'e-mails intégré, Supabase n'envoie que 2 e-mails par heure, et ce plafond ne se modifie qu'avec votre propre serveur SMTP. Au lancement, c'est le premier mur : les confirmations d'inscription n'arrivent plus. Les autres limites (connexions, vérifications, inscriptions anonymes) sont réglables sur la même page.
Repérez aussi les fonctions qui coûtent à chaque appel : API d'IA, e-mail, SMS. Aucune limite propre à votre application ne les protège par défaut.
Le risque. Un robot qui crée des milliers de comptes ou appelle en boucle votre fonction OpenAI : la facture arrive avant l'alerte. Les mécanismes de ce dérapage sont détaillés dans notre article sur les factures d'API LLM qui explosent.
Corriger. Configurez un SMTP transactionnel (Resend, Postmark, Brevo) et relevez la limite d'envoi à un niveau raisonnable. Activez Enable CAPTCHA protection (hCaptcha ou Cloudflare Turnstile) dans les réglages d'authentification pour protéger l'inscription, la connexion et la réinitialisation. Dans chaque fonction qui coûte de l'argent, ajoutez un compteur par utilisateur et par période, stocké dans une table, et refusez l'appel au-delà.
Ce que cette check-list ne couvre pas
Ces huit points ferment les portes visibles, pas la logique métier : un montant de commande modifiable avant paiement, un webhook Stripe accepté sans vérifier la signature. Ces failles-là se trouvent en lisant le code. Nous avons décrit les défauts récurrents du code généré, au-delà de la sécurité, dans ce qu'il faut reprendre sur un MVP codé par IA.
Le scan de sécurité de Lovable ne remplace pas ce contrôle : la plateforme rappelait à The Register qu'il appartient à l'utilisateur d'en appliquer les recommandations. Matt Palmer reprochait au scanner sorti en avril 2025 de vérifier seulement qu'une règle RLS existe, pas qu'elle soit correcte ni adaptée à la logique de l'application.
Par où commencer
Si vous n'avez qu'une heure : le Security Advisor, la recherche de clés secrètes dans les sources du site, le test curl sans connexion sur vos trois tables les plus sensibles, puis le plan Pro pour les sauvegardes.
Si un test renvoie des données qu'il ne devrait pas, ou si vous ne savez pas interpréter le résultat, faites relire l'application avant l'ouverture. C'est ce que fait notre audit de reprise d'application Lovable : sécurité, qualité du code, ce qui manque pour la production, et un verdict sous 48 h, garder et renforcer ou refaire. Pour un projet construit avec un autre outil, la démarche est la même dans notre offre de reprise de projet IA.
Sujets
- #saas
- #mvp
- #sécurité
- #conformité


