Ce que c'est et pourquoi ça compte
Beaucoup d'apps faites avec Claude Code utilisent un backend tout prêt, comme Supabase ou Firebase. C'est pratique : l'app parle directement à la base de données, depuis le navigateur ou le téléphone, sans serveur à vous entre les deux.
Pour cela, l'app emporte deux choses avec elle : l'adresse du projet et une clé publique. N'importe qui ouvrant votre app peut les voir, il suffit de regarder les requêtes dans le navigateur. Et c'est normal : cette clé est faite pour être là. Ce n'est pas un mot de passe, elle dit seulement « cette requête vient de mon app ».
La question, c'est ce qu'il y a derrière la porte. Ce qui décide qui peut lire quoi, ce n'est pas la clé : ce sont les règles sur la base de données. Sur Supabase, elles s'appellent Row Level Security (sécurité au niveau des lignes), sur Firebase règles de sécurité. En clair : pour chaque table, vous écrivez qui peut lire, ajouter, modifier et supprimer quelles lignes. Par exemple : « chaque utilisateur ne voit que ses commandes ».
Si ces règles manquent, ou sont écrites trop larges, la clé publique ouvre tout. Celui qui copie l'adresse et la clé depuis le navigateur peut lire les tables : e-mails, noms, messages, tout ce que vous y avez mis. Parfois même les modifier. Pas besoin d'être un hacker : une ligne dans le terminal suffit.
Claude crée les tables et l'app fonctionne, parce que vous, une fois connecté, vous recevez les données. Qu'elles arrivent aussi à quelqu'un qui n'a aucun compte, vous ne le voyez pas en utilisant l'app. Vous ne le voyez que si vous le testez.
Avant tout : uniquement sur votre app
Ce test, vous le faites sur votre projet, avec vos données ou avec des utilisateurs de test. Jamais sur les apps ou les bases de données des autres, même pas « juste pour voir » : entrer dans un système sans autorisation peut être un délit, en Italie comme dans beaucoup d'autres pays. Si vous pensez que l'app de quelqu'un d'autre est ouverte, ne l'essayez pas : écrivez à ceux qui la gèrent.
1. Le test, en deux minutes
Trois parties : la première trouve la clé, la deuxième regarde ce que voit quelqu'un qui n'est pas connecté, la troisième si un utilisateur lit les données d'un autre. Je les décris sur Supabase ; pour Firebase, regardez plus bas.
a. Trouvez la clé
- Ouvrez votre app dans le navigateur, sans vous connecter.
- Clic droit > Inspecter > onglet Réseau (Network), puis rechargez la page.
- Cherchez les requêtes vers la base de données. Sur Supabase, ce sont des adresses qui se terminent par .supabase.co.
- Cliquez sur une requête : vous y voyez l'adresse du projet et la clé publique (publishable ou anon).
Si vous ne trouvez pas de requêtes vers la base de données, allez sur une page qui charge des données et regardez à nouveau.
b. Essayez de lire sans être connecté
Sans être connecté, ouvrez un terminal et lancez cette ligne. Mettez votre projet, votre clé publique et le nom d'une de vos tables :
curl "https://VOTRE-PROJET.supabase.co/rest/v1/NOM_TABLE?select=*" -H "apikey: CLE_PUBLIQUE"
Si des lignes d'utilisateurs reviennent, la porte est ouverte. Si vous obtenez une liste vide, [], ou une erreur de permission, cette table ne répond pas au premier venu. Deux précautions : vérifiez que la table contient vraiment des données, sinon une liste vide ne vous dit rien. Et répétez-le pour chaque table, pas seulement pour une.
c. Testez en tant qu'utilisateur A
Créez deux utilisateurs de test, A et B, et faites enregistrer quelque chose à chacun : un profil, une note, une commande. Ensuite connectez-vous en tant que A et copiez son jeton d'accès : vous le trouvez dans l'onglet Réseau, dans l'en-tête Authorization des requêtes faites après la connexion. Puis lancez :
curl "https://VOTRE-PROJET.supabase.co/rest/v1/NOM_TABLE?select=*" -H "apikey: CLE_PUBLIQUE" -H "Authorization: Bearer JETON_DE_A"
Les deux en-têtes n'ont pas le même rôle. La clé publique va toujours dans apikey. Authorization: Bearer ne porte que le jeton d'accès d'un utilisateur connecté, jamais la clé publique et jamais la clé secrète. C'est pour ça que la première ligne n'a que apikey : là, il n'y a aucun utilisateur.
Si les lignes de B sont dedans, A lit B. C'est le trou le plus facile à rater : la porte est fermée à ceux qui ne sont pas connectés, mais une fois dedans, chacun voit tout. Traitez le jeton de A comme un mot de passe : avec lui, vous êtes A. Utilisez-le uniquement pour le test et ne le collez nulle part.
Si vous ne voulez pas le faire à la main
Demandez-le à Claude, dans votre projet. Il fait les requêtes à votre place :
Fais le test toi-même : avec la clé publique et sans connexion, essaie de lire chaque table ; ensuite, avec deux utilisateurs de test, vérifie si A voit les données de B. Dis-moi table par table ce qui est ouvert. Ne modifie rien.
Si Claude Code vous demande la permission de lancer une commande, lisez ce qu'il va faire avant de dire oui. « Ne modifie rien » sert précisément à ça : le test lit, il n'écrit pas.
2. Le prompt qui règle le problème
Quand vous savez ce qui est ouvert, fermez-le. Voici le prompt que j'utilise :
Chaque utilisateur ne voit que ses propres lignes. Active la Row Level Security sur chaque table et écris les règles : lecture, insertion, modification et suppression uniquement sur les lignes de l'utilisateur connecté. Les tables vraiment publiques (par ex. un catalogue) en lecture seule. Vérifie aussi les vues, les fonctions RPC et les buckets Storage. Liste-moi chaque élément avec la règle que tu as mise, puis refais le test et montre-moi que A ne voit plus B.
Les mots sont choisis. « Lecture, insertion, modification et suppression » parce que sur Supabase, les règles s'écrivent pour chaque opération : si vous ne fermez que la lecture, quelqu'un peut encore écrire ou supprimer. « Les vues, les fonctions RPC et les buckets Storage » parce que les données ne sortent pas que des tables. Sur Supabase, une vue, par défaut, passe outre les règles des tables qu'elle lit ; une fonction peut être écrite de façon à les contourner ; les fichiers envoyés ont leurs propres règles. Et « refais le test » parce qu'une règle se vérifie en l'essayant, pas en la lisant.
3. Deux choses à savoir
Mieux vaut une porte fermée
Sur Supabase, avec la Row Level Security activée et aucune règle écrite, rien ne passe avec la clé publique : mieux vaut une porte fermée qu'une porte ouverte. Ensuite, vous n'ouvrez que ce qui est nécessaire. Si après la correction un écran ne charge plus de données, ne désactivez pas la sécurité : il manque une règle. Demandez à Claude d'ajouter celle-là, rien de plus.
Les deux clés
- La clé publique (publishable, ou anon dans les projets plus anciens) peut être dans l'app et dans le navigateur. À elle seule, elle n'ouvre que ce que les règles permettent.
- La clé secrète (secret ou service_role) passe outre toutes les règles : uniquement sur le serveur, jamais dans l'app, jamais sur GitHub.
Si la clé secrète s'est retrouvée dans l'app ou dans un dépôt, considérez-la comme grillée. Créez-en une nouvelle dans le tableau de bord de Supabase, mettez-la à la place de l'ancienne partout où il le faut, et seulement ensuite supprimez ou désactivez l'ancienne. La retirer du code ne suffit pas : elle reste dans l'historique et chez ceux qui l'ont déjà vue.
Cherche dans le code de l'app, et dans tout ce qui finit dans le navigateur ou dans l'app publiée, si la clé secrète de Supabase (secret ou service_role) s'y trouve. Vérifie aussi l'historique git. Dis-moi où tu la trouves. Ne modifie rien.
Si vous utilisez Firebase
Même principe, là-bas elles s'appellent règles de sécurité. Sur Firebase aussi, la clé de l'app n'est pas un secret : elle sert à identifier le projet. Ce qui protège les données, ce sont les règles.
La règle à chercher, c'est celle qui laisse passer tout le monde, du genre allow read, write: if true. La documentation de Firebase dit de ne jamais l'utiliser en production. À sa place, il faut des règles qui vérifient qui est connecté et à qui appartient le document.
Lis les règles de sécurité Firebase de mon projet. Dis-moi quelles collections peuvent être lues ou écrites par quelqu'un qui n'est pas connecté, et si un utilisateur peut lire les documents d'un autre. Ensuite, propose-moi des règles où chaque utilisateur ne voit et ne modifie que ses propres données. Ne publie rien tant que je ne te l'ai pas dit.
Les erreurs les plus courantes
- Faire le test sur une seule table. Chaque table a ses propres règles : testez-les toutes.
- Ne fermer que la lecture. Écrivez aussi les règles pour l'insertion, la modification et la suppression.
- Fermer les tables et oublier les vues, les fonctions et les fichiers. Vérifiez-les aussi, avec le même test.
- Une règle qui laisse tout lire à n'importe qui de connecté. Le bon contrôle porte sur qui possède la ligne, pas sur le fait d'être connecté.
- Désactiver la sécurité quand un écran ne marche plus. Ajoutez la règle qui manque au lieu de tout rouvrir.
- Mettre la clé secrète dans l'app ou dans le dépôt. Utilisez-la uniquement sur le serveur et, si elle a fuité, remplacez-la.
Check-list
- J'ai fait le test sans connexion sur chaque table.
- Avec deux utilisateurs de test, A ne voit pas les données de B.
- Chaque table a des règles pour la lecture, l'insertion, la modification et la suppression.
- J'ai aussi vérifié les vues, les fonctions RPC et les buckets Storage.
- Les tables publiques sont en lecture seule.
- La clé secrète se trouve uniquement sur le serveur.
- J'ai refait le test après la correction.
Questions fréquentes
Est-ce que je peux faire ce test sur l'app de quelqu'un d'autre, par curiosité ?
Non. Uniquement sur votre app. Lire les données d'un système sans autorisation peut être un délit, même si la porte est ouverte. Si vous soupçonnez un problème, prévenez ceux qui gèrent l'app.
Après la correction, l'app ne charge plus certaines données. Qu'est-ce que j'ai fait de travers ?
Probablement rien : la porte est maintenant fermée et il manque une règle pour ce cas. Dites à Claude quel écran ne charge pas et demandez-lui la règle la plus stricte qui suffit, puis refaites le test avec A et B.
Est-ce que je dois le refaire de temps en temps ?
À chaque fois que vous ajoutez une table ou que vous changez qui peut voir quoi. Les règles valent table par table, et une nouvelle table peut naître sans aucune.