Blog
Architecture
9 octobre 20259 min

Rate limiting et JWT WebSocket : sécuriser avant le lancement

J'ai une conviction : la securite se fait avant le lancement, pas apres. Attendre d'avoir des utilisateurs pour securiser son app, c'est comme installer une serrure apres le cambriolage. Voici comment j'ai implemente le rate limiting et l'authentification JWT sur les WebSockets de TAMSIV — avant meme d'avoir un seul utilisateur.

Points cles a retenir :
- Le rate limiting HTTP (100 req/15min/IP) et WebSocket (10 conn/min + 60 msg/min/user) protegent contre les abus et les couts imprevus.
- L'authentification JWT est requise des la connexion WebSocket — pas apres.
- Securiser tot coute moins cher en temps et en argent que securiser tard.
- Chaque message WebSocket peut declencher 3 APIs payantes — le rate limiting protege ton budget.

Pourquoi securiser une app avant d'avoir des utilisateurs ?

La reponse courte : parce que les couts. Chaque appel WebSocket dans TAMSIV peut potentiellement declencher trois APIs payantes : Deepgram pour le STT, OpenRouter pour le LLM, et OpenAI pour le TTS. Sans rate limiting, un script malveillant — ou meme un simple bug — pourrait generer des centaines d'euros de facture en une nuit.

La reponse longue : parce que l'architecture. Ajouter le rate limiting apres coup, c'est refactorer du code partout. Le faire des le debut, c'est un middleware propre qui s'integre naturellement dans le pipeline vocal.

Et si tu commences par "on verra plus tard" pour la securite, tu ne le feras jamais. C'est un principe que j'ai appris en construisant TAMSIV en tant que developpeur solo — chaque raccourci technique se paie au centuple.

Bouclier de securite numerique holographique flottant au-dessus de serveurs dans un datacenter moderne
La securite n'est pas une feature optionnelle — c'est un prerequis architectural.

Comment implementer le rate limiting HTTP sur Express ?

Le backend Express de TAMSIV expose quelques endpoints REST (generation d'images, push notifications, administration). Sans protection, ces endpoints sont vulnerables au brute force et au DDoS.

J'ai configure un rate limiter simple mais efficace : 100 requetes par 15 minutes par IP. Voici le raisonnement :

  • 100 requetes : suffisant pour un usage normal (un utilisateur actif fait rarement plus de 20 requetes en 15 minutes), avec une marge pour les pics
  • 15 minutes : fenetre glissante. Plus court (1 minute) serait trop restrictif. Plus long (1 heure) laisserait passer trop de requetes abusives.
  • Par IP : la methode la plus simple et la plus universelle. Les solutions basees sur le user-id ne protegent pas contre les utilisateurs non-authentifies.

En pratique, le middleware express-rate-limit s'installe en 5 lignes. Le header Retry-After informe le client du temps d'attente. Le code HTTP 429 (Too Many Requests) est la reponse standard. C'est une recommandation OWASP que tout backend devrait implementer.

Pourquoi le rate limiting WebSocket est-il un vrai defi ?

Le HTTP, c'est facile. Le WebSocket, c'est une autre histoire. La connexion est persistante et les messages arrivent en flux continu. Tu ne peux pas simplement compter les "requetes" — chaque segment audio genere un message, et le STT envoie des resultats intermediaires en streaming.

J'ai mis en place deux niveaux de protection :

  1. 10 connexions par minute par utilisateur : empeche le flood de connexions. Un utilisateur normal ouvre 1-2 connexions par session. 10 est une marge confortable qui attrape les scripts.
  2. 60 messages par minute par utilisateur : limite le debit de messages sur une connexion active. Ca semble beaucoup, mais en streaming audio, les chunks arrivent vite.
Ecran de terminal affichant des logs de rate limiting et un dashboard de metriques de requetes API
Le monitoring des requetes bloquees permet d'ajuster les seuils en fonction de l'usage reel.

Pourquoi 60 messages par minute ? Parce que pendant une conversation vocale active, le client envoie des chunks audio toutes les secondes. Avec le STT natif ou Deepgram, les resultats intermediaires s'ajoutent. 60 messages laissent une marge de securite tout en bloquant les abus evidents.

La difficulte supplementaire : le comptage doit etre par utilisateur, pas par connexion. Un utilisateur malveillant pourrait ouvrir 9 connexions et envoyer 59 messages sur chacune, contournant un rate limit par connexion. Le comptage par user-id (extrait du JWT) resout ce probleme.

Comment securiser l'authentification JWT sur les WebSockets ?

L'authentification WebSocket est fondamentalement differente du HTTP. Avec HTTP, tu envoies le token dans le header Authorization a chaque requete. Avec WebSocket, l'authentification se fait une seule fois, a la connexion.

Dans TAMSIV, le token JWT Supabase est requis des la connexion :

ws://backend:3001?token=eyJhbGciOiJIUzI1NiIs...

Le serveur verifie le token avant d'accepter la connexion. Si le token est invalide, expire ou absent : connexion refusee immediatement. Pas de message d'erreur detaille (pour eviter le leaking d'informations), juste un code de fermeture WebSocket 4401.

Pourquoi le token est dans le query string et pas dans un header ? Parce que l'API WebSocket du navigateur ne supporte pas les headers personnalises a la connexion. C'est une limitation connue du protocole. L'alternative est d'envoyer le token comme premier message, mais ca laisse une fenetre ou la connexion est non-authentifiee — inacceptable pour TAMSIV.

Le token est aussi disponible via le header Authorization: Bearer xxx pour les clients qui le supportent (comme les bibliotheques Node.js). Le serveur accepte les deux methodes.

Ecran de laptop montrant un diagramme de flux d'authentification avec validation de token JWT
Le JWT est verifie a la connexion — aucun message n'est traite sur une connexion non-authentifiee.

Quels sont les couts reels d'une faille de securite pour un dev solo ?

Pour une startup ou un dev solo, une faille de securite n'est pas qu'un probleme technique — c'est un probleme financier. Voici les scenarios que le rate limiting previent :

  • Scenario 1 — Bug client : une boucle infinie cote frontend envoie des milliers de messages WebSocket. Sans rate limiting : facture API explosive. Avec : 60 messages max, puis blocage.
  • Scenario 2 — Script malveillant : quelqu'un utilise l'API pour generer du contenu via le LLM. Sans rate limiting : utilisation illimitee a tes frais. Avec : bloque apres 100 requetes/15min.
  • Scenario 3 — DDoS leger : un bot martele les endpoints. Sans rate limiting : backend surcharge, app inaccessible. Avec : les requetes excessives sont rejetees avant d'atteindre la logique metier.

Le systeme d'alertes admin que j'ai mis en place envoie un email via Resend quand un utilisateur atteint 80% du rate limit. Ca me permet d'intervenir avant que le probleme ne devienne critique.

Comment tester la securite d'une application avant le lancement ?

L'audit de securite de TAMSIV a couvert plusieurs axes :

  1. Rate limiting verification : scripts de test qui envoient des rafales de requetes et verifient que le blocage se declenche au bon seuil.
  2. JWT validation : tests avec tokens expires, malformes, modifies (tampering), et tokens d'autres projets Supabase.
  3. RLS policies : verification que chaque table du schema de base de donnees a des politiques d'acces correctes. Plus de 30 politiques RLS testees individuellement.
  4. Input sanitization : verification que les messages WebSocket sont valides et que les injections sont bloquees.
  5. CORS configuration : seuls les domaines autorises (tamsiv.com, IPs de dev) peuvent se connecter au backend.

Ces tests font partie de la suite de tests automatises du backend. Chaque deploiement via railway up verifie que la securite n'a pas regresse.

Quelles sont les bonnes pratiques de securite pour les WebSockets en production ?

Voici la checklist que j'ai suivie pour TAMSIV, basee sur les recommandations OWASP :

  • Authentification a la connexion : JWT verifie avant d'accepter le upgrade WebSocket
  • Rate limiting a deux niveaux : connexions et messages, les deux par utilisateur
  • Validation des messages : chaque message est valide (format, taille maximale, type) avant traitement
  • Timeouts de securite : connexions inactives fermees apres 5 minutes. L'AudioPlayerService a aussi un timeout de 30 secondes pour le cleanup.
  • Logging des anomalies : chaque rate limit atteint, chaque tentative de connexion echouee est logguee pour analyse
  • CORS strict : liste blanche d'origines autorisees, mise a jour a chaque changement de domaine
  • TLS obligatoire : en production, seul wss:// est accepte

Cette approche securite-d'abord s'aligne avec la philosophie de clean architecture du projet. La securite n'est pas une couche ajoutee — c'est un composant architectural au meme titre que le routage ou la base de donnees.

Quel impact le rate limiting a-t-il sur l'experience utilisateur ?

Un rate limiting bien configure est invisible pour l'utilisateur normal. Les seuils sont calibres pour qu'un usage legitime ne les atteigne jamais. Mais quand un utilisateur les atteint, l'experience doit rester claire :

  • Cote HTTP : code 429 avec header Retry-After et message explicatif
  • Cote WebSocket : message d'erreur type avant la fermeture de connexion
  • Cote frontend : notification non-bloquante informant l'utilisateur de ralentir

Le systeme de notifications gere ces cas gracieusement. L'utilisateur sait ce qui se passe sans etre bloque brutalement.

FAQ

Le rate limiting affecte-t-il les conversations vocales en streaming ?

Non, pour un usage normal. Le seuil de 60 messages par minute est calibre pour accommoder le streaming audio et les resultats intermediaires du STT. Un utilisateur qui parle normalement ne depasse jamais ce seuil. Si le seuil est atteint, c'est le signe d'un bug client ou d'un usage abusif.

Comment TAMSIV gere-t-il les tokens JWT expires pendant une session ?

Le token JWT Supabase a une duree de vie d'une heure. Le client frontend rafraichit le token automatiquement avant expiration. Si la connexion WebSocket utilise un token expire, le serveur renvoie un code de fermeture specifique et le client se reconnecte avec un nouveau token.

Est-ce que le rate limiting est configurable par utilisateur ?

Les seuils par defaut s'appliquent a tous les utilisateurs. Le dashboard admin permet de voir les utilisateurs qui approchent des limites et d'ajuster les seuils globaux si necessaire. Une evolution prevue permettra des seuils differencies par plan d'abonnement (Free vs Pro vs Team).

Pourquoi ne pas utiliser un service tiers comme Cloudflare pour le rate limiting ?

Cloudflare gere bien le rate limiting HTTP mais ne couvre pas les WebSockets de maniere granulaire. Le rate limiting applicatif dans TAMSIV permet un controle fin par utilisateur et par type de message, ce qu'un WAF externe ne peut pas offrir. Les deux approches sont complementaires.

Comment verifier que le rate limiting fonctionne correctement ?

Le backend inclut des tests automatises qui simulent des rafales de requetes et verifient que les reponses HTTP 429 arrivent au bon seuil. Les logs de production montrent le nombre de requetes bloquees par jour, ce qui permet d'ajuster les seuils en continu.