Sécuriser la lecture avec la RLS
Comprendre pourquoi la clé publishable est publique, puis protéger les tables avec la Row Level Security.
Objectifs de la séance
- comprendre pourquoi la clé de l'application est publique et ce que cela implique ;
- activer la RLS (Row Level Security) sur les trois tables ;
- écrire des policies de lecture avec
create policy ... for select ... using (...); - tester la sécurité comme un attaquant, avec la commande
curl; - ne jamais mettre de clé secrète dans une application.
Petite vérification
Avant de commencer, vérifiez si vous savez...
- dans le SQL Editor, exécuter
select count(*) from public.question;et lire le résultat - écrire le texte « l'hameçonnage » (avec son apostrophe) sous la forme acceptée dans une instruction SQL
insert - dans le SQL Editor, afficher le plus grand identifiant de la table
questionavec la fonctionmax
📌 Réponse attendue
Notions théoriques
Une clé publique, forcément
Pour interroger la base, votre application enverra des requêtes à l'API de Supabase en présentant une clé. Supabase en propose deux sortes :
| Clé | Forme | Usage |
|---|---|---|
| publishable | sb_publishable_... | dans l'application : elle est publique par nature |
| secret | sb_secret_... | uniquement sur un serveur de confiance : elle contourne toutes les protections |
Une application mobile ou Web est téléchargée par ses utilisateurs : n'importe qui peut l'ouvrir
et lire la clé qu'elle contient. La clé publishable est donc connue de tous, y compris des
personnes malveillantes. Elle ne donne que les droits du rôle anon (un visiteur non connecté).
Une clé secret (ou l'ancienne clé service_role) donne tous les droits sur la base. La mettre
dans une application, c'est l'offrir à tous les utilisateurs. Si vous la voyez dans du code d'une
application ou dans un dépôt GitHub, considérez-la comme compromise : il faut la révoquer.
L'API REST de Supabase
Supabase expose chaque table du schéma public sous une adresse Web :
https://VOTRE_PROJET.supabase.co/rest/v1/theme
Quiconque connaît cette adresse et la clé publishable peut envoyer des requêtes, sans passer par
votre application (avec un navigateur, avec curl, avec un script...). Les contrôles faits dans
l'application ne protègent donc de rien : la protection doit être dans la base.
La RLS : un filtre ligne par ligne
La RLS (Row Level Security, « sécurité au niveau des lignes ») est un mécanisme de PostgreSQL qui décide, pour chaque ligne, qui a le droit de la lire ou de la modifier.
- On l'active sur une table :
alter table public.theme enable row level security; - Une fois activée, la table est fermée par défaut : sans règle, personne (hors administrateur) ne voit de ligne et n'en modifie.
- On ouvre ensuite précisément ce qui est autorisé, avec des policies (politiques).
create policy "Nom de la règle"
on public.ma_table for select -- quelle table, quelle opération
to anon, authenticated -- pour quels rôles
using (condition); -- quelles lignes
for select: la règle concerne la lecture (insert,updateetdeleteexistent aussi) ;to anon, authenticated: les rôles concernés ;using (condition): la condition évaluée pour chaque ligne.using (true)autorise toutes les lignes ;using (est_active)n'autorise que celles où la colonneest_activeest vraie.
Avec la RLS activée, une opération qui n'a aucune policy est interdite. En lecture, la table
paraît simplement vide : l'API renvoie une liste vide [] sans message d'erreur. Les écritures
sans policy sont, elles, refusées avec une erreur.
Droits et RLS : deux verrous
| Verrou | Question posée | Mise en place |
|---|---|---|
grant | Ce rôle a-t-il le droit de lire cette table ? | grant select ... (séance 20) |
| RLS | Quelles lignes ce rôle peut-il voir ? | enable row level security + create policy |
Le SQL Editor, lui, utilise un rôle d'administrateur qui ignore la RLS : c'est pour cela que vous
y voyez toutes les lignes, quelles que soient les policies. Pour tester les droits d'un visiteur, il
faut interroger l'API comme le ferait un visiteur : c'est le rôle de curl.
Notre politique de lecture
| Table | Qui peut lire ? | Qui peut écrire ? |
|---|---|---|
theme | tout le monde | personne (via l'API) |
question | tout le monde, mais seulement les questions actives | personne (via l'API) |
reponse | tout le monde | personne (via l'API) |
Seul l'administrateur (vous, dans le tableau de bord) peut modifier les questions.
À ce stade, la colonne est_correcte est lisible par tous : un joueur malin peut donc lire les
bonnes réponses avant de jouer. Vous corrigerez cette faille au tutoriel 8.
TP pour réfléchir et résoudre des problèmes
Votre mission : écrire le script sql/03_rls_lecture.sql, l'exécuter, puis vérifier qu'un
visiteur peut lire les questions mais pas les modifier.
Étape 1 — Activer la RLS sur les trois tables
Dans VS Code, créez sql/03_rls_lecture.sql. Commencez par un commentaire qui explique le pourquoi,
puis activez la RLS sur les trois tables :
Un commentaire de tête qui rappelle que la clé est publique évite qu'un futur développeur « simplifie » le script en désactivant la RLS pour faire disparaître une erreur.
Étape 2 — Autoriser la lecture des thèmes
Le nom d'une policy apparaît dans le tableau de bord. Un nom clair (« Lecture publique des thèmes ») permet de comprendre la règle sans lire le code.
Étape 3 — Ne montrer que les questions actives
La colonne est_active permet de retirer une question du jeu sans la supprimer. La policy doit
donc filtrer les lignes : seules celles dont est_active est vraie sont visibles.
Si le filtre « questions actives » n'était fait que dans l'application, un visiteur qui interroge directement l'API verrait aussi les questions retirées du jeu. Dans la policy, le filtre s'applique à tous les accès.
Étape 4 — Autoriser la lecture des réponses
Le script ne contient aucune policy d'écriture. C'est voulu : tout ce qui n'est pas autorisé explicitement reste interdit.
Étape 5 — Exécuter le script et le tester avec curl
Collez le script complet dans le SQL Editor et cliquez sur Run. Vérifiez, dans le menu Authentication > Policies (ou dans Table Editor), que les trois tables affichent leurs policies.
Vous allez maintenant jouer le rôle d'un visiteur malveillant avec la commande curl, qui envoie
une requête HTTP depuis le terminal. Récupérez d'abord :
- l'URL du projet : Project Settings > Data API (de la forme
https://VOTRE_PROJET.supabase.co) ; - la clé publishable : Project Settings > API Keys (elle commence par
sb_publishable_).
Utilisez le terminal Git Bash de VS Code (menu déroulant du terminal) : dans PowerShell, curl
désigne une autre commande, et la gestion des guillemets est différente.
Test 1 : la lecture fonctionne. Cette requête lit les thèmes :
curl "https://VOTRE_PROJET.supabase.co/rest/v1/theme?select=code,libelle" -H "apikey: sb_publishable_VOTRE_CLE"
Vous obtenez une liste JSON de trois thèmes.
Test 2 : l'écriture est refusée. Cette requête tente d'ajouter un thème « pirate » :
curl -X POST "https://VOTRE_PROJET.supabase.co/rest/v1/theme" -H "apikey: sb_publishable_VOTRE_CLE" -H "Content-Type: application/json" -d '{"code":"pirate","libelle":"Pirate"}'
La réponse est une erreur : le code est 42501 (droits insuffisants), avec un message du genre
« new row violates row-level security policy » ou « permission denied for table theme ». Le thème
pirate n'a pas été créé : vérifiez-le dans Table Editor.
Ne vous contentez jamais de « ça marche dans mon application » : essayez ce qu'un attaquant essaierait. Si le test d'écriture réussissait, ce serait la preuve d'une faille, à corriger avant d'aller plus loin.
Étape 6 — Mettre à jour le README et enregistrer
Dans sql/README.md, précisez la description du script 03 (les questions actives) :
| `03_rls_lecture.sql` | Row Level Security : lecture publique des questions actives |
Le README décrit ce que fait chaque script : le modifier dans le même commit que le script évite qu'il ne raconte plus la même chose que le code.
📌 Une solution
Test de mémorisation/compréhension
Exercices complémentaires
Exercice A : tester les questions actives
Dans le SQL Editor, passez la colonne est_active d'une question à false, avec l'instruction
update public.question set est_active = false where id = 1;. Lisez ensuite les questions par curl
(.../rest/v1/question?select=id&id=eq.1) : la liste est vide. Remettez ensuite est_active à
true avec la même instruction.
Exercice B : lire les réponses comme un visiteur
Lisez les réponses avec curl (.../rest/v1/reponse?select=id,texte,est_correcte&limit=4).
Que remarquez-vous au sujet de la colonne est_correcte ? Notez votre observation : elle servira
au tutoriel 8.
Ce qu'il faut retenir
| Notion | Résumé |
|---|---|
| Clé publishable | Clé publique, présente dans l'application : elle ne donne que les droits du visiteur. |
| Clé secret / service_role | Contourne la sécurité : jamais dans une application ni dans un dépôt. |
| RLS | Filtre ligne par ligne ; une fois activée, tout ce qui n'est pas autorisé est interdit. |
| Policy | Règle create policy ... for select to ... using (...) qui autorise une opération sur certaines lignes. |
| Deux verrous | grant (droit sur la table) et RLS (droit sur les lignes). |
Test avec curl | Interroger l'API comme un visiteur : lecture autorisée, écriture refusée (42501). |
Aperçu de la prochaine séance
La base est créée, remplie et protégée. Il est temps de brancher l'application dessus : dans la prochaine séance, vous allez installer le client Supabase, ranger l'adresse et la clé du projet dans un fichier d'environnement qui ne sera jamais publié, puis lire vos premiers thèmes depuis un écran.