Règles d'accès Supabase et Firebase (CWE-862) : comment l'absence de Row Level Security expose les données et comment corriger
Les applications Supabase et Firebase laissent le navigateur interroger directement la base avec une clé publique : entre un visiteur et chaque ligne, il n'y a que la Row Level Security ou les Security Rules. Une table créée en SQL sans enable row level security, ou une règle Firestore laissée à allow read, write: if true, est lisible et modifiable par n'importe qui. Comment les politiques USING et WITH CHECK limitent les lignes à l'utilisateur connecté, avec un exemple de migration PostgreSQL.
Explication en Langage Simple (ELI5)
Un immeuble de bureaux remet à chaque visiteur la même carte du hall, imprimée au dos de la brochure. Cela ne pose pas de problème tant que la porte de chaque étage vérifie qui vous êtes avant de s'ouvrir. Un étage a été aménagé à la hâte et sa porte n'a aucun lecteur : la carte du hall l'ouvre et n'importe qui peut entrer lire les dossiers. La clé anon de Supabase est la carte du hall : elle est faite pour être publique. La Row Level Security est le lecteur sur la porte de chaque étage. La correction consiste à installer un lecteur sur chaque porte et à lui indiquer précisément quels dossiers chacun peut voir.
Concepts Clés et Termes
- Clé anon publique
- La clé
anonde Supabase et la configuration web de Firebase sont livrées dans le bundle de l'application, et n'importe qui peut les copier. Elles identifient le projet, pas l'utilisateur : ce ne sont pas des secrets et elles ne protègent pas les données à elles seules. - Row Level Security (RLS)
- Une fonctionnalité de PostgreSQL qui filtre chaque requête à travers des politiques. Le Table Editor de Supabase l'active pour les nouvelles tables, mais les tables créées en SQL ou par migration démarrent sans RLS, et les droits par défaut laissent alors
anonetauthenticatedlire et modifier toutes les lignes. - USING et WITH CHECK
USINGdétermine quelles lignes existantes un utilisateur peut voir, modifier ou supprimer ;WITH CHECKdétermine quelles lignes nouvelles ou modifiées il peut écrire. Une politique n'ayant queUSINGsur insert ou update permet d'écrire des lignes pour une autre organisation.- Clé service_role
- La clé
service_rolecontourne entièrement la RLS. Elle n'a sa place que sur des serveurs et dans des edge functions, jamais dans un navigateur ou une application mobile. - Règles Firestore en mode test
- Les projets démarrés en mode test reçoivent des règles qui autorisent toutes les lectures et écritures jusqu'à une date, et beaucoup finissent en
allow read, write: if true. Les règles doivent comparerrequest.auth.uidau champ propriétaire du document.
Déroulement de l'Attaque Étape par Étape
L'attaquant ouvre l'application
L'URL Supabase et la clé anon se trouvent dans le bundle JavaScript ou le paquet de l'application mobile.
Il interroge directement la table
Il appelle l'endpoint REST de invoices avec la seule clé anon, sans passer par les écrans de l'application. Une requête vers un projet de test avec la clé anon et sans session utilisateur illustre le cas.
La base renvoie toutes les lignes
La RLS est désactivée et anon possède le droit select, donc PostgreSQL n'applique aucun filtre et renvoie les factures de toutes les organisations.
Des données fuient ou sont modifiées
Avec update et delete accordés aussi, l'attaquant peut modifier ou effacer des enregistrements. La CVE-2025-48757 a été attribuée en 2025 après que des chercheurs ont découvert qu'une RLS manquante exposait les données de plus de 170 applications générées par un créateur d'applications à base d'IA.
Code Source : Vulnérable vs Sécurisé
-- migration.sql : la RLS n'est jamais activée sur cette table
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- la clé anon publique peut lire, modifier et supprimer toutes les lignes
grant select, insert, update, delete on public.invoices to anon, authenticated;
-- migration.sql : RLS activée, anon révoqué, une politique par commande
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- une fois la RLS activée, sans politique il n'y a aucun accès
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;
-- les utilisateurs ne voient que les factures de leurs organisations
create policy "members read their org's invoices"
on public.invoices for select to authenticated
using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
-- les nouvelles lignes doivent appartenir à une organisation de l'utilisateur ; pas de politique update ni delete
create policy "members add invoices to their org"
on public.invoices for insert to authenticated
with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
Liste de Contrôle de Sécurité pour l'Ingénierie
- Activez la RLS sur chaque table des schémas exposés, et faites échouer la CI quand
select tablename from pg_tables where schemaname = 'public' and not rowsecurityrenvoie une ligne. - Écrivez une politique par commande, limitée
to authenticated, avecUSINGpour les lectures etWITH CHECKpour les insertions et mises à jour ; n'ajoutez pas de politiques update ou delete inutiles. - Retirez les droits de la table à
anonquand les utilisateurs anonymes n'ont aucune raison de lire, et gardez la cléservice_roleuniquement dans les environnements serveur. - Activez la RLS sur les tables que lisent vos politiques, comme
memberships, pour que les utilisateurs ne puissent pas s'ajouter à une autre organisation. - Testez avec deux utilisateurs de test : l'utilisateur A doit obtenir zéro ligne pour l'organisation de l'utilisateur B ; pour Firestore, exécutez des tests unitaires de règles sur l'émulateur avec
@firebase/rules-unit-testing.