Aller au contenu principal

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...

  1. dans le SQL Editor, exécuter select count(*) from public.question; et lire le résultat
  2. écrire le texte « l'hameçonnage » (avec son apostrophe) sous la forme acceptée dans une instruction SQL insert
  3. dans le SQL Editor, afficher le plus grand identifiant de la table question avec la fonction max
📌 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éFormeUsage
publishablesb_publishable_...dans l'application : elle est publique par nature
secretsb_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é).

La clé secrète n'a rien à faire dans une application

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.

  1. On l'active sur une table : alter table public.theme enable row level security;
  2. 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.
  3. 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, update et delete existent 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 colonne est_active est vraie.
Pas de policy, pas d'accès

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​

VerrouQuestion poséeMise en place
grantCe rôle a-t-il le droit de lire cette table ?grant select ... (séance 20)
RLSQuelles 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​

TableQui peut lire ?Qui peut écrire ?
themetout le mondepersonne (via l'API)
questiontout le monde, mais seulement les questions activespersonne (via l'API)
reponsetout le mondepersonne (via l'API)

Seul l'administrateur (vous, dans le tableau de bord) peut modifier les questions.

Un point à retenir pour le tutoriel 8

À 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 :


Bonne pratique - Expliquer le « pourquoi » dans le script

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​


Bonne pratique - Nommer une policy comme une phrase

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.


Bonne pratique - Filtrer dans la policy, pas dans l'application

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​


Bonne pratique - Ouvrir le strict nécessaire

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_).
Sous Windows

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.


Bonne pratique - Tester comme un attaquant

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 |

Bonne pratique - Garder la documentation à jour

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​


Pourquoi la clé publishable est-elle considérée comme publique ?


Que se passe-t-il quand on active la RLS sur une table qui n'a aucune policy ?


Que fait la condition « using (est_active) » dans la policy sur question ?


Quelle clé ne doit JAMAIS apparaître dans le code d'une application ?


Quel code d'erreur obtient-on en essayant d'insérer une ligne sans y avoir droit ?


Pourquoi voit-on toutes les lignes dans le SQL Editor malgré la RLS ?



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​

NotionRésumé
Clé publishableClé publique, présente dans l'application : elle ne donne que les droits du visiteur.
Clé secret / service_roleContourne la sécurité : jamais dans une application ni dans un dépôt.
RLSFiltre ligne par ligne ; une fois activée, tout ce qui n'est pas autorisé est interdit.
PolicyRègle create policy ... for select to ... using (...) qui autorise une opération sur certaines lignes.
Deux verrousgrant (droit sur la table) et RLS (droit sur les lignes).
Test avec curlInterroger 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.