Boilerplate-Stack
Retour au blog
Articles

Bonnes pratiques d'authentification et sécurité SaaS en 2025

|
4 min de lecture

La sécurité n'est pas une fonctionnalité, c'est un prérequis

En 2025, les cyberattaques contre les applications SaaS sont en hausse constante. Injection SQL, XSS, CSRF, credential stuffing — les vecteurs d'attaque sont nombreux et les conséquences d'une faille sont désastreuses : perte de données, amendes RGPD, réputation détruite.

La bonne nouvelle ? La majorité de ces attaques peuvent être prévenues avec des pratiques établies. Boilerplate-Stack intègre ces protections nativement, conformément aux recommandations OWASP.

Magic links vs mots de passe : le bon choix en 2025

Les mots de passe sont le maillon faible de la sécurité web depuis des décennies :

  • Les utilisateurs réutilisent les mêmes mots de passe partout
  • Les bases de données de mots de passe sont régulièrement compromises
  • Le stockage sécurisé (bcrypt, argon2) ne protège pas contre le phishing
  • La gestion du mot de passe oublié est un vecteur d'attaque en soi

Les magic links éliminent tous ces problèmes :

  • Pas de mot de passe à stocker — rien à compromettre dans votre base
  • Authentification par email — si l'attaquant a accès à l'email, il a déjà accès à tout via « mot de passe oublié »
  • Expérience utilisateur supérieure — un clic, pas de mémorisation
  • Compatible avec l'OAuth — ajoutez Google, GitHub en complément

Dans Boilerplate-Stack, les magic links sont envoyés via Brevo avec des templates personnalisés en français et en anglais.

Authentification côté serveur : getUser() vs getSession()

C'est l'erreur la plus dangereuse et la plus courante en développement Supabase :

// DANGEREUX — la session peut être falsifiée côté client
const { data: { session } } = await supabase.auth.getSession()

// SÉCURISÉ — valide le token auprès de Supabase
const { data: { user } } = await supabase.auth.getUser()

getSession() lit simplement le cookie sans le valider. Un attaquant peut forger un cookie de session. getUser() envoie le token à Supabase pour vérification. Utilisez toujours getUser() côté serveur.

Row Level Security (RLS) : votre dernière ligne de défense

La RLS PostgreSQL est le mécanisme de sécurité le plus puissant dans une architecture Supabase. Elle s'applique au niveau de la base de données, indépendamment du code applicatif.

-- Chaque table filtrée par membership
CREATE POLICY "members_access" ON chat_sessions FOR ALL
USING (EXISTS (
  SELECT 1 FROM memberships
  WHERE memberships.account_id = chat_sessions.account_id
  AND memberships.user_id = auth.uid()
));

Avantages de la RLS :

  • Défense en profondeur : même si une route API est compromise, les données restent protégées
  • Impossible à contourner : s'applique à toutes les requêtes, y compris les jointures
  • Performance : PostgreSQL optimise les politiques RLS comme des WHERE classiques

OWASP Top 10 : les protections implémentées

1. Injection (SQL, NoSQL, OS)

Supabase utilise des requêtes paramétrées nativement. Jamais de concaténation de strings SQL. Validation Zod sur tous les inputs.

2. Authentification défaillante

Magic links + OAuth. Pas de mots de passe. Rate limiting strict sur les endpoints d'auth (5 requêtes/minute).

3. Exposition de données sensibles

RLS sur chaque table. Sélection explicite des colonnes (.select()). Jamais de SELECT * exposé à l'utilisateur.

4. XXE (Entités externes XML)

Non applicable — pas de parsing XML. API JSON uniquement.

5. Contrôle d'accès défaillant

Middleware de sécurité API avec niveaux (apiSecurity.public(), .authenticated(), .admin()). Vérification de rôle et de membership.

6. Mauvaise configuration de sécurité

Headers HTTP stricts dans next.config.ts :

X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
Strict-Transport-Security: max-age=63072000

7. Cross-Site Scripting (XSS)

React échappe par défaut. Content Security Policy stricte. Sanitisation des inputs avec sanitize.html().

8. Désérialisation non sécurisée

Validation Zod sur toutes les entrées. Pas de eval(), pas de désérialisation arbitraire.

9. Composants vulnérables connus

Dépendances à jour. Audit régulier avec npm audit.

10. Journalisation insuffisante

Table admin_logs pour les actions administratives. ai_requests pour le suivi d'utilisation. credit_transactions pour l'audit financier.

Rate limiting multi-niveaux

Le rate limiting est essentiel pour prévenir les abus :

strict:   5/min   — opérations sensibles (auth, suppression)
standard: 30/min  — API générale
relaxed:  100/min — lectures publiques
ai:       10/min  — requêtes IA
contact:  3/min   — formulaire de contact
webhook:  100/min — webhooks externes

Implémenté avec Upstash Redis en production, avec un fallback en mémoire pour le développement.

Protection CSRF

Le pattern Double Submit Cookie protège contre les attaques CSRF :

  1. Un token aléatoire est généré et stocké dans un cookie httpOnly
  2. Le même token est envoyé dans le header de la requête
  3. Le serveur vérifie que les deux correspondent
  4. Validation additionnelle de l'Origin et du Referer

Protection contre les bots

Cloudflare Turnstile est intégré sur les formulaires sensibles :

  • Formulaire de contact
  • Inscription newsletter
  • Page de connexion (optionnel)

Turnstile est invisible pour l'utilisateur dans la plupart des cas, contrairement aux CAPTCHA traditionnels.

Ne partez pas de zéro pour la sécurité

Implémenter correctement toutes ces couches de sécurité prend des semaines et nécessite une expertise pointue. Une seule erreur peut compromettre l'ensemble.

Boilerplate-Stack intègre toutes ces protections nativement. Chaque route API, chaque composant, chaque requête base de données est sécurisé selon les meilleures pratiques OWASP.

Découvrez l'implémentation complète sur boilerplate-stack.com et lancez votre SaaS avec une sécurité de niveau professionnel dès le premier jour.