Row Level Security Supabase : fonctionnement, politiques et limites
Comprendre le fonctionnement du Row Level Security (RLS) dans PostgreSQL et Supabase, l'écriture des politiques d'accès et pourquoi RLS activé ne garantit pas la sécurité.
Sur cette page (13 sections)▼
Le Row Level Security (RLS) est un mécanisme de sécurité natif du moteur de base de données PostgreSQL qui filtre les lignes renvoyées ou modifiées par une requête en fonction de l'identité de l'utilisateur qui l'exécute. Dans une architecture moderne ou une application développée avec Supabase, le client frontend communique souvent directement avec la base via PostgREST. Sans RLS rigoureux, n'importe quel visiteur connaissant votre clé publique d'API peut lire, modifier ou supprimer les lignes de vos tables.
Activer RLS sur une table est indispensable, mais « RLS activé » ne signifie pas automatiquement « données protégées ». Une politique trop permissive ou mal conçue peut laisser les données tout aussi accessibles qu'en l'absence totale de protection.
Le rôle de PostgreSQL et de Supabase
Pour comprendre RLS, il faut distinguer l'infrastructure de données de la couche d'accès :
- PostgreSQL est le moteur qui stocke les données et évalue les règles de sécurité ligne par ligne lors de chaque instruction SQL.
- Supabase fournit une couche d'authentification (
auth.users) et une interface API automatique (PostgREST). Supabase transmet le jeton d'authentification (JWT) à PostgreSQL sous la forme de variables de session (notammentauth.uid()). - Les politiques (policies) sont des expressions booléennes attachées à une table qui déterminent si une ligne donnée peut être lue (
SELECT), créée (INSERT), modifiée (UPDATE) ou supprimée (DELETE).
Lorsque RLS est activé sur une table sans aucune politique définie, PostgreSQL applique un refus par défaut strict : personne ne peut lire ni écrire, à l'exception des rôles d'administration privilégiés.
Le rôle PostgreSQL service_role et l'attribut BYPASSRLS
Une distinction fondamentale doit être posée d'emblée : dans l'environnement PostgreSQL géré par Supabase, le rôle service_role possède l'attribut BYPASSRLS.
Dans PostgreSQL, les rôles disposant de l'attribut BYPASSRLS ou du statut de superutilisateur ne sont pas soumis aux politiques de filtrage au niveau des lignes. Dans Supabase, la clé secrète service_role génère des requêtes authentifiées sous ce rôle privilégié pour permettre les opérations d'administration, les tâches d'arrière-plan ou les migrations. Si cette clé est utilisée dans votre code frontend ou divulguée côté client, les politiques RLS définies sur la base ne s'appliquent pas à ces requêtes, conférant un accès direct aux données.
La clé service_role doit impérativement être réservée aux environnements de confiance côté serveur (fonctions serverless sécurisées, scripts d'administration) et ne doit jamais être exposée sur une surface publique. Pour les requêtes exécutées pour le compte d'un utilisateur connecté depuis le serveur, privilégiez l'utilisation du client standard avec le jeton JWT de l'utilisateur afin de préserver l'évaluation continue de RLS.
Comment fonctionnent les politiques : USING et WITH CHECK
Une politique RLS s'articule autour de deux clauses majeures :
1. La clause USING
La clause USING est appliquée pour filtrer les lignes existantes avant qu'une opération ne soit effectuée. Elle s'applique aux commandes SELECT, UPDATE et DELETE. Si l'expression retourne true pour une ligne donnée, cette ligne est visible et manipulable par la commande. Si elle retourne false, la ligne est invisible (pour un SELECT) ou introuvable (pour un UPDATE ou DELETE).
2. La clause WITH CHECK
La clause WITH CHECK contrôle les nouvelles lignes insérées ou les nouvelles versions de lignes modifiées. Elle s'applique aux commandes INSERT et UPDATE. Elle garantit qu'un utilisateur ne peut pas créer ou transformer une ligne d'une manière qui violerait la règle de sécurité (par exemple, s'attribuer l'identifiant d'un autre utilisateur lors de la création d'un document).
Si aucune clause WITH CHECK n'est spécifiée lors d'un UPDATE, l'expression de la clause USING est automatiquement réutilisée pour valider la nouvelle version de la ligne.
-- Exemple : politique équilibrée pour une table de documents
CREATE POLICY "Les utilisateurs gèrent uniquement leurs propres documents"
ON public.documents
FOR ALL
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
Différence entre lecture et écriture
Les risques et les impacts diffèrent selon le type d'opération :
- Lectures publiques non désirées (
SELECT) : Une politiqueUSING (true)sur une table de profils ou de commandes permet à tout visiteur de récupérer l'ensemble de la base via un appel API REST standard. - Créations usurpées (
INSERT) : Une politiqueWITH CHECK (true)autorise n'importe quel compte authentifié à insérer des lignes au nom d'un autre utilisateur ou d'une autre organisation. - Modifications destructives (
UPDATE,DELETE) : Une clauseUSINGabsente ou incomplète sur ces opérations peut permettre à un utilisateur A d'écraser ou supprimer les ressources d'un utilisateur B (vulnérabilité de type IDOR — Insecure Direct Object Reference).
Les erreurs fréquentes dans les applications modernes
Lors des audits de code de projets vibe codés ou assemblés rapidement avec l'IA, quatre configurations défaillantes reviennent régulièrement :
1. La politique ouverte par complaisance
Pour résoudre une erreur d'affichage ou un statut HTTP 403 lors du développement, un assistant IA ou un créateur crée fréquemment une règle de ce type :
-- À PROSCRIRE sur des données privées
CREATE POLICY "Accès complet aux données"
ON public.projets
FOR ALL
USING (true);
Cette règle désactive en pratique toute étanchéité : la table redevient publique.
2. Confondre utilisateur authentifié et propriétaire de la ressource
La règle TO authenticated USING (true) vérifie que la personne possède un compte sur votre service, mais ne vérifie aucunement si la ligne consultée lui appartient. Tous vos clients connectés peuvent alors consulter les projets de tous les autres clients.
3. L'absence de vérification sur les colonnes sensibles
Même lorsque l'accès à une ligne est restreint au propriétaire, une mise à jour (UPDATE) sans contrôle des colonnes peut permettre à l'utilisateur de modifier un champ role ou is_admin présent sur sa propre ligne.
4. L'oubli d'activation de RLS sur une table nouvellement créée
Créer des politiques sur une table sans avoir exécuté au préalable ALTER TABLE nom_table ENABLE ROW LEVEL SECURITY; est inefficace : PostgreSQL ignore les politiques tant que le mécanisme RLS n'est pas formellement activé sur la table.
Comment tester méthodiquement ses politiques RLS
Valider visuellement une interface dans le navigateur ne suffit pas : votre session locale masque généralement les permissions réelles.
Pour tester efficacement vos politiques :
- Test anonyme : Effectuez une requête
curlou utilisez un client REST avec votre cléanonsans en-têteAuthorization. Vérifiez que seules les données intentionnellement publiques sont renvoyées. - Le test des deux comptes : Connectez-vous avec un compte de test A, récupérez l'identifiant d'une ressource privée créée par A, puis tentez d'y accéder ou de la modifier depuis une session avec un compte de test B. L'API doit retourner une réponse vide ou une erreur 404/403.
- Inspection dans le Studio Supabase : Utilisez l'éditeur SQL pour simuler des requêtes sous l'identité d'un utilisateur précis :
-- Simuler la session d'un utilisateur spécifique dans PostgreSQL
SET LOCAL ROLE authenticated;
SET LOCAL "request.jwt.claims" = '{"sub": "00000000-0000-0000-0000-000000000000"}';
SELECT * FROM public.documents;
Ce que GVO observe et ce qu'il ne peut pas garantir
Dans le cadre d'un audit de projet, GVO examine les fichiers du référentiel de code pour identifier la surface d'exposition liée à Supabase :
- Ce que GVO peut observer : GVO analyse les fichiers de migration SQL, les schémas de base de données déclarés, les configurations client et les chemins d'appel aux méthodes Supabase. Il relève l'absence d'activation démontrable de RLS sur les tables déclarées, les politiques visiblement trop permissives (
USING (true)), ainsi que l'utilisation de clésservice_roledans des contextes inadaptés. - Ce que GVO ne peut pas garantir : Une analyse statique du code ne peut pas préjuger des politiques créées manuellement directement dans le dashboard de production sans être archivées dans le code, ni garantir l'absence totale d'effets de bord liés à des fonctions stockées complexes (
SECURITY DEFINER) non soumises à l'audit.
Pour mettre en pratique ces vérifications pas à pas sur votre projet, consultez notre Guide pour sécuriser une application Supabase.
Vérifier ce point sur votre projet
GVO confronte le code et les configurations de votre application aux règles de sécurité réelles.