Le problème
Un petit restaurant a besoin de plus qu’une page menu : réservations de table, devis pour événements, galerie photo et actualités, le tout modifiable sans appeler un développeur.
Les formulaires publics d’un petit commerce sont aussi la première chose que trouvent les bots : la sécurité devait faire partie du projet, pas venir après.
Du formulaire visiteur à la boîte mail du gérant
- 01Formulaire
- 02Validation Zod
- 03Limiteur de débit
- 04Supabase + RLS
- 05Email au gérant
Sécurité intégrée
| CONTRÔLE | COUCHE | PROTÈGE |
|---|---|---|
| Politiques RLS sur chaque table | Base de données | Accès aux données |
| Espace admin derrière un middleware d’authentification | Application | Back-office |
| Validation de schéma sur chaque champ | API | Injection, spam |
| Limitation de débit sur les endpoints publics | API | Abus |
| Clé service-role uniquement côté serveur | Secrets | Fuite de clé |
| Content-Security-Policy et en-têtes de sécurité | HTTP | Clickjacking, XSS |
Directement depuis le code
Extraits du vrai dépôt et des livrables, raccourcis pour la lecture.
ALTER TABLE public.reservations ENABLE ROW LEVEL SECURITY;
CREATE POLICY "public can insert reservations"
ON public.reservations FOR INSERT
TO anon, authenticated
WITH CHECK (true);
CREATE POLICY "admins can manage reservations"
ON public.reservations FOR ALL
TO authenticated
USING (is_admin());Sécurité au niveau des lignes : un visiteur peut seulement créer une réservation, jamais en lire une. Lecture et gestion sont réservées aux admins.
import { RateLimiterMemory } from 'rate-limiter-flexible'
// NOTE: In-memory limiters reset on cold starts in serverless environments.
// For production, swap to RateLimiterRedis or RateLimiterPostgres.
const limiters = {
reservation: new RateLimiterMemory({ points: 5, duration: 3600 }),
devis: new RateLimiterMemory({ points: 3, duration: 3600 }),
contact: new RateLimiterMemory({ points: 5, duration: 3600 }),
} as constLimites de débit par endpoint sur les formulaires publics. Le commentaire signale la limite connue, reprise dans le retour d’expérience.
Résultats
Ce que je ferais autrement
Les clients ne demandent pas de RLS, mais ils en souffrent quand elle manque. Prochaine étape : remplacer le limiteur en mémoire par un stockage partagé pour tenir la montée en charge.