Sécuriser un SaaS multi-tenant : guide complet contre les failles IDOR et fuites RLS
Comment détecter et corriger les failles IDOR et les erreurs RLS sur PostgreSQL et Supabase. Exemples de code Next.js et requêtes SQL pour isoler hermétiquement les données de chaque client.
Les failles IDOR (Insecure Direct Object References) représentent le risque n°1 des applications SaaS multi-tenants. Elles permettent à un utilisateur authentifié d'accéder aux données d'un autre utilisateur en modifiant simplement un identifiant dans la requête. Ce guide détaille les patterns de sécurisation indispensables sous Next.js et Supabase RLS.
1. Qu'est-ce qu'une faille IDOR en environnement SaaS ?
L'IDOR (aussi appelée BOLA par l'OWASP) est une vulnérabilité de contrôle d'accès au niveau des objets.
Imaginons une URL du type `/api/projects/123/export`. Si l'utilisateur connecté appartient à l'entreprise B et que le projet 123 appartient à l'entreprise A, le serveur ne doit pas se contenter de vérifier que l'identifiant 123 existe : il doit valider formellement que l'utilisateur connecté possède les droits sur l'organisation détentrice de ce projet.
L'utilisation d'UUID v4 au lieu d'ID numériques séquentiels réduit la prédictibilité, mais NE CORRIGE PAS la faille : si un attaquant obtient l'UUID (via un log, une fuite ou un lien partagé), il peut toujours accéder à la ressource.
2. Les 3 erreurs critiques courantes sur Supabase RLS
Sur Supabase et PostgreSQL, les développeurs commettent souvent ces erreurs d'inattention :
- **La fonction SECURITY DEFINER non sécurisée :** Une fonction PostgreSQL en `SECURITY DEFINER` s'exécute avec les privilèges de son créateur (souvent postgres), contournant ainsi toutes les politiques RLS existantes si elle n'intègre pas un contrôle strict de `auth.uid()`.
- **La policy SELECT sans équivalent en UPDATE :** Vous protégez la lecture des données, mais oubliez de restreindre la modification. Un concurrent peut alors écraser vos données sans pouvoir les lire.
- **Les jointures sur des tables non protégées :** Si la table A est protégée par RLS mais que la table B liée par clé étrangère ne l'est pas, un utilisateur peut extraire les données de A via une requête imbriquée sur B.
-- Exemple de policy RLS robuste multi-tenant sur Supabase
CREATE POLICY "Users can only access records belonging to their tenant"
ON public.documents
FOR ALL
TO authenticated
USING (
tenant_id IN (
SELECT org_id FROM public.memberships
WHERE user_id = auth.uid()
)
);3. Pattern de sécurisation en Next.js Server Actions
Dans Next.js (App Router), ne faites jamais confiance aux paramètres passés par le client dans une Server Action. Validez toujours la session et l'appartenance de la ressource côté serveur avant toute mutation.
// Pattern sécurisé de Server Action avec vérification de propriété
export async function deleteProject(projectId: string) {
const session = await getSession();
if (!session?.userId) throw new Error("Unauthorized");
// Vérifier la propriété de la ressource avant suppression
const project = await db.project.findUnique({
where: { id: projectId },
select: { organizationId: true },
});
if (!project || project.organizationId !== session.organizationId) {
throw new Error("Forbidden: Access Denied");
}
return await db.project.delete({ where: { id: projectId } });
}Questions fréquentes
Les UUID protègent-ils contre les failles IDOR ?
Non. Les UUID rendent l'identifiant difficile à deviner, mais si l'identifiant fuite (dans un email, une URL partagée ou un log), quiconque le possède peut accéder à la ressource si le serveur ne vérifie pas la propriété.
Comment tester les failles IDOR sur mon SaaS ?
Créez deux comptes de test dans deux organisations distinctes (Compte A et Compte B). Récupérez l'identifiant d'un objet appartenant au Compte A, et tentez de le lire ou de le modifier depuis la session du Compte B.
Votre SaaS est-il prêt pour la production ?
Identifiez vos failles critiques avant vos utilisateurs avec la Security Review Validra sous 5 jours ouvrés.