La règle que personne n'a écrite : lire une table Supabase sans Row Level Security

Sécurité Web & API Niveau 3/4 ~5 min 8 novembre 2026

Le défi

Un profil demande deux colonnes à Supabase. Demande toutes les colonnes de chaque ligne. Rapporte la clé API de l'admin.

Ce que tu vas apprendre

  • Lire une URL PostgREST : table, liste select, filtres
  • Expliquer pourquoi la clé anon de Supabase est publique et ce qui protège réellement les données
  • Élargir une requête de deux colonnes d'une seule ligne à toute la table
  • Distinguer un nom affiché choisi par l'utilisateur d'un rôle défini côté serveur
  • Verrouiller une table Supabase avec RLS, une vue publique et des column grants

Compétences testées

Test d'APIBroken access controlManipulation de requêtes

Prérequis

  • Ce qu'est une query string
  • Ce que sont une table de base de données et ses colonnes

Comment ça marche

Supabase place une API REST (PostgREST) devant Postgres. Le navigateur lui parle directement : l'URL nomme la table (/rest/v1/profiles), les colonnes (select=display_name,avatar_url) et les lignes (id=eq.<uuid>). Chaque requête porte la clé anon du projet, un JWT avec "role":"anon" livré dans le JavaScript de l'application.

Cette clé est publique par conception. Ce qui protège les données, c'est le Row Level Security : des politiques écrites dans Postgres qui décident, par rôle, quelles lignes une requête peut voir. Avec RLS désactivé sur une table que le rôle anon peut lire, l'URL est la seule limite, et c'est le navigateur qui écrit l'URL. Une page qui demande deux colonnes d'une seule ligne, c'est une convention, pas un contrôle.

En 2025, des chercheurs ont découvert que des applications générées avec Lovable, un outil de création d'applications par IA, livraient souvent des tables sans RLS (CVE-2025-48757) : n'importe qui avec la clé publique pouvait lister les utilisateurs, les e-mails et les clés API stockées.

Erreurs fréquentes

  • S'arrêter à select=*. Avec le filtre id=eq. toujours en place, tu obtiens toutes les colonnes de la seule ligne de Sam, et Sam est un membre. Supprime le filtre pour voir les autres lignes.
  • Prendre la ligne appelée Admin. display_name est ce qu'un utilisateur a tapé à l'inscription. Cette ligne a "role":"member" ; l'admin est la ligne avec "role":"admin".
  • Répondre avec la clé de Sam. C'est la première clé que tu vois, mais elle appartient au profil affiché à l'écran.
  • Forger un token service_role. Modifier le JWT casse sa signature HS256, et PostgREST répond PGRST301. Aucune forge n'est nécessaire : la clé anon lit déjà tout.
  • Retirer le header apikey. La gateway de Supabase refuse la requête avant que PostgREST ne la voie.

Comment s'en protéger

  • Active RLS sur chaque table d'un schéma exposé : alter table profiles enable row level security; Le Security Advisor de Supabase signale les tables où il est désactivé.
  • Écris des politiques explicites, par exemple chaque propriétaire lit sa propre ligne : create policy "own row" on profiles for select to authenticated using (auth.uid() = id);
  • Expose les champs publics via une vue ou une fonction qui ne renvoie que display_name et avatar_url, ou révoque les privilèges de colonne pour anon.
  • Garde les secrets hors des tables lisibles par le navigateur : stocke les clés API hachées, dans un schéma que la Data API n'expose pas, et fais tourner toute clé qui a déjà été lisible.
  • Ne décide jamais d'un privilège à partir de champs modifiables par l'utilisateur, comme un nom affiché.

Solution complète

Les membres Pro et Max débloquent la solution complète étape par étape.

Passer Pro

Statistiques de la communauté

132 résolutions
71% taux de réussite
M2F14M3 Premier sang

Hacks du jour associés

38 000+ Hackers Vrais labs Gratuit
Commencer Gratuitement ou résolvez le hack du jour, sans compte