ScanVibeScanVibe
·8 min read·ScanVibe Team

Comment sécuriser votre app Supabase en 10 minutes

supabasesecurityguide

Comment sécuriser votre app Supabase en 10 minutes

Supabase est le backend de référence pour les vibe coders. Lovable, Bolt et des dizaines d'outils de coding IA utilisent Supabase par défaut pour l'authentification, la base de données et le stockage. C'est rapide à mettre en place, généreux sur le plan gratuit, et parfaitement compatible avec des frameworks comme Next.js et React.

Mais il y a un problème : la plupart des apps Supabase sont déployées avec des erreurs de configuration de sécurité critiques. Nous avons scanné des centaines d'applications en production avec ScanVibe, et les mêmes problèmes reviennent sans cesse.

La bonne nouvelle ? Vous pouvez tous les corriger en environ 10 minutes. Voici comment.

Temps estimé : 10 minutes pour parcourir ce guide. 5 sections, chacune avec un problème clair et une solution prête à copier-coller.

1. Activez Row Level Security (RLS) sur chaque table

78% des apps Supabase que nous scannons ont au moins une table sans RLS activé

C'est le problème de sécurité Supabase numéro 1 que nous constatons. Sans RLS, n'importe qui possédant votre anon key peut lire et écrire chaque ligne de votre base de données.

Le problème

Lorsque vous créez une table dans Supabase, RLS est désactivé par défaut. Cela signifie que votre API key anon — qui est intégrée dans votre JavaScript frontend — donne un accès complet à toutes les données.

-- Check which tables have RLS disabled
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public';

La solution

Activez RLS sur chaque table, puis ajoutez des policies pour définir ce qui doit être accessible :

-- Enable RLS
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;

-- Allow users to read their own profile
CREATE POLICY "Users can read own profile"
  ON public.profiles FOR SELECT
  USING (auth.uid() = id);

-- Allow users to update their own profile
CREATE POLICY "Users can update own profile"
  ON public.profiles FOR UPDATE
  USING (auth.uid() = id);
Règle générale : Si une table a RLS désactivé et qu'il ne s'agit pas d'une table de référence purement publique, c'est une vulnérabilité.

Patterns courants

Pour les tables qui doivent être lisibles publiquement mais modifiables uniquement par leurs propriétaires :

-- Anyone can read
CREATE POLICY "Public read" ON public.posts
  FOR SELECT USING (true);

-- Only the author can insert/update/delete
CREATE POLICY "Author can modify" ON public.posts
  FOR ALL USING (auth.uid() = author_id);

Pour les tables privées (ex. : paramètres utilisateur, informations de paiement) :

-- Only authenticated users can access their own rows
CREATE POLICY "Private access" ON public.user_settings
  FOR ALL USING (auth.uid() = user_id);

2. Arrêtez d'exposer votre clé service_role

Votre projet Supabase possède deux clés :

anon Utilisable dans le navigateur. Limitée par les policies RLS.
service_role Contourne TOUTES les RLS. Ne jamais exposer côté frontend.

Le problème

Nous trouvons fréquemment la clé service_role dans :

Si quelqu'un obtient votre clé service_role, il dispose d'un accès administrateur complet à votre base de données. Il peut tout lire, écrire et supprimer.

La solution

  1. Vérifiez vos variables d'environnement. La clé service_role ne doit se trouver que dans les variables d'environnement côté serveur (pas de préfixe NEXT_PUBLIC_ dans Next.js).
# WRONG — exposed to the browser
NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY=eyJhb...

# RIGHT — server-side only
SUPABASE_SERVICE_ROLE_KEY=eyJhb...
  1. Utilisez la clé anon dans votre client frontend :
// Frontend client — uses anon key
import { createClient } from '@supabase/supabase-js';

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
  1. Utilisez la clé service_role uniquement dans le code côté serveur (API routes, server actions) :
// Server-side only — never import this in client code
import { createClient } from '@supabase/supabase-js';

const supabaseAdmin = createClient(
  process.env.SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!
);

3. Sécurisez vos buckets de stockage

Le problème

De nombreuses applications créent des buckets publics ou ont des storage policies qui permettent à n'importe qui de télécharger ou d'écraser des fichiers. Nous avons constaté :

La solution

-- Only allow users to access their own files
CREATE POLICY "Users access own files" ON storage.objects
  FOR ALL USING (
    bucket_id = 'avatars'
    AND auth.uid()::text = (storage.foldername(name))[1]
  );
Bonnes pratiques pour le stockage :
• Utilisez des dossiers spécifiques par utilisateur : avatars/{user_id}/photo.jpg
• Définissez des limites de taille de fichier dans votre logique d'upload
• Validez les types de fichiers côté serveur
• Utilisez des signed URLs pour les fichiers privés au lieu de rendre les buckets publics

4. Configurez correctement l'authentification

Supabase Auth est solide par défaut, mais il existe des erreurs de configuration courantes.

Désactivez les confirmations par email avec précaution

Si vous désactivez les confirmations par email (courant en phase de développement), pensez à les réactiver avant le passage en production. Sans confirmation, n'importe qui peut créer des comptes avec de fausses adresses email.

Restreignez les URLs de redirection

Dans votre tableau de bord Supabase sous Authentication > URL Configuration :

# Add only your production domain and localhost
https://yourdomain.com/**
http://localhost:3000/**
N'utilisez pas de wildcards comme https://* — cela permet des attaques par redirection où un attaquant peut voler des tokens d'authentification en redirigeant vers son propre domaine.

Activez le rate limiting

Supabase dispose d'un rate limiting intégré pour les endpoints d'authentification. Assurez-vous qu'il est activé :


5. Auditez vos fonctions de base de données

Si vous utilisez des Edge Functions ou des fonctions de base de données Supabase, elles peuvent contourner RLS si elles utilisent SECURITY DEFINER.

Le problème

-- This function runs with the creator's permissions, not the caller's
CREATE FUNCTION get_all_users()
RETURNS SETOF public.profiles
LANGUAGE sql
SECURITY DEFINER  -- Dangerous: bypasses RLS
AS $$
  SELECT * FROM public.profiles;
$$;

La solution

Utilisez SECURITY INVOKER sauf si vous avez une raison spécifique de ne pas le faire :

CREATE FUNCTION get_user_profile(user_id uuid)
RETURNS public.profiles
LANGUAGE sql
SECURITY INVOKER  -- Safe: respects RLS
AS $$
  SELECT * FROM public.profiles WHERE id = user_id;
$$;

Si vous devez utiliser SECURITY DEFINER, ajoutez des vérifications de permissions explicites à l'intérieur de la fonction.


Checklist rapide

Parcourez cette liste en 10 minutes :


Automatisez tout avec ScanVibe

Vous ne voulez pas vérifier tout cela manuellement à chaque déploiement ? ScanVibe scanne votre application Supabase et détecte automatiquement tous ces problèmes. Nous vérifions :

RLS Détection de Row Level Security
Keys API keys exposées dans le frontend
Auth Faiblesses des endpoints d'authentification
+5 Autres catégories de sécurité

Un scan. 30 secondes. Tous les problèmes détectés.


Pour aller plus loin

Articles similaires

Scannez votre app maintenant

Vérifiez la sécurité de votre app construite avec l'IA en quelques secondes. Gratuit, sans inscription.

Lancer un scan