Sécurité - rocambille/start-express-react GitHub Wiki

Cette page décrit les mécanismes de sécurité mis en place dans StartER : protection CSRF, stratégie contre le XSS, gestion des cookies, politique d'authentification, ainsi que les bonnes pratiques recommandées. L'objectif est de fournir un socle cohérent, minimaliste mais robuste, adapté à une architecture front React + API Express totalement découplée et stateless.

CSRF (Cross-Site Request Forgery)

Dans l'architecture de StartER :

  • l'API est stateless,
  • le serveur ne maintient aucune session côté back,
  • StartER ne configure pas explicitement CORS. Par défaut, aucune requête cross-site n'est autorisée, car le serveur ne renvoie aucun Access-Control-Allow-Origin.
  • le front est servi sur le même domaine que le back : c'est le seul domaine autorisé à communiquer avec l'API,
  • les cookies sont en SameSite=strict.

Cela bloque déjà la majorité des attaques CSRF classiques, puisqu'un site tiers ne peut ni envoyer les cookies stricts, ni interagir avec l'API.

StartER utilise une protection additionnelle, notamment pour les requêtes mutatives (POST, PUT, PATCH, DELETE) : le pattern Client-Side Double-Submit.

Tip

Le pattern est particulièrement bien expliqué dans la FAQ du package csrf-csrf, avec d'autres éléments de réponse sur les attaques CSRF, la nécessité ou non de mettre en place des protections...

Le pattern est également mentionné entre autres dans la documentation de Symfony.

Pour compléter sur le sujet, nous vous recommandons de lire la documentation de l'OWASP.

Génération d'un jeton côté client

Le front génère un jeton CSRF par l'utilitaire csrfToken() dans src/react/components/utils :

  • le jeton est stocké dans un cookie __Host-x-csrf-token (voir la documentation de l'OWASP sur le préfixe __Host-),
  • il expirera au bout de 30 secondes,
  • mais chaque utilisation prolonge son expiration, reproduisant le comportement d'un cookie de session côté client,
  • aucun stockage serveur n'est nécessaire.

Ce mécanisme permet de dire "la session CSRF est active tant qu'il y a de l'activité", tout en ayant un timeout explicite pour éviter les jetons périmés conservés trop longtemps.

Chaque requête mutative inclut un en-tête contenant la même valeur que celle stockée dans le cookie : c'est le "double-submit".

X-CSRF-Token: <token>

Vérification côté serveur

Le serveur :

  1. intercepte les requêtes POST/PUT/PATCH/DELETE,
  2. lit le cookie __Host-x-csrf-token,
  3. compare avec l'en-tête x-csrf-token,
  4. répond avec le statut 401 en cas d'absence du header ou d'incohérence entre le header et le cookie.

Le serveur reste entièrement stateless : il compare simplement deux valeurs transmises par le client, sans maintenir de session ni stocker de token côté back.

Cette approche est acceptable tant que :

  • L'API n'est pas exposée à des tiers (pas de cross-site autorisé).
  • SameSite=strict élimine la possibilité qu'un site tiers envoie les cookies nécessaires.

Protection contre le XSS

L'essentiel des protections XSS repose sur le front :

  • aucune interpolation non sécurisée n'est faite dans le DOM,
  • React empêche par défaut les injections HTML,
  • StartER n'utilise dangerouslySetInnerHTML nulle part.

Du côté backend :

  • les réponses JSON sont servies avec un Content-Type: application/json, ce qui empêche leur interprétation comme du HTML ou du JavaScript exécutable,
  • toutes les données stockées en base sont traitées comme du contenu opaque côté front.

Conseils supplémentaires

Pour les projets qui souhaitent aller plus loin :

  • mettre en place une Content-Security-Policy (CSP) stricte,
  • bloquer toute inline-script,
  • n'autoriser que des sources explicites pour les scripts et styles.

StartER n'impose pas de CSP par défaut, mais la structure en front + API s'y prête bien.

Cookies, authentification et intégrité

StartER utilise deux cookies principaux :

  • __Host-auth : jeton JWT signé, portant l'identité utilisateur
  • __Host-x-csrf-token : jeton CSRF éphémère renouvelé côté client

Les deux sont :

Le cookie __Host-auth est en HttpOnly, ce qui le rend innaccessible par du code JS côté client. Ce n'est pas le cas pour le token CSRF qui est écrit côté client.

Jeton d'accès (JWT)

Le JWT stocké en cookie ne représente pas une session mais une attestation signée. Sa durée de vie doit rester courte, afin de limiter la fenêtre d'usage d'un jeton compromis.

Le JWT n'est pas chiffré, uniquement signé. Il contient des données qui sont lisibles par le client, mais protégées contre la modification.

Points importants :

  • le serveur ne stocke pas d'état de session,
  • la révocation se fait par expiration ou demande explicite au serveur de supprimer le cookie (logout).

Limitation des surfaces d'attaque

Pas de cookies cross-site

StartER ne sert aucun contenu depuis un domaine tiers, ni depuis un sous-domaine différent. Cela réduit drastiquement les risques CSRF ainsi que les attaques liées aux requêtes provenant d'autres sites.

API stateless

L'absence de session côté serveur limite les risques de :

  • session fixation,
  • cleanup incomplet,
  • dépassement de mémoire pour suivi de sessions longues.

Pas d'upload direct non contrôlé

Par défaut, StartER ne traite pas l'upload de fichiers. Si ajouté, il doit être filtré, stocké hors du document root et validé (type, taille...).

Stratégies HTTP recommandées

Les déploiements devraient systématiquement inclure :

  • Strict-Transport-Security
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • Referrer-Policy: strict-origin-when-cross-origin

Le serveur de production (Caddy, Nginx ou autre) est l'endroit adapté pour ces en-têtes.

Gestion des erreurs et messages de sécurité

Les erreurs renvoyées par le back sont volontairement sobres :

  • 400 pour les requêtes mal formées,
  • 401 pour les problèmes d'authentification,
  • 403 pour les problèmes d'autorisation,
  • 500 pour les erreurs non gérées côté serveur,
  • pas de message détaillé exposant la nature de la défaillance (token manquant, signature invalide, cookie expiré...).

Le front affiche ensuite des messages clairs pour l'utilisateur sans révéler d'informations sensibles.

Recommandations additionnelles

Journalisation

Il est recommandé de logger côté serveur les erreurs 401/403, ce qui inclut :

  • les échecs d'authentification,
  • les tentatives d'accès non authentifiées,
  • les accès interdits.

Surveillance des mutations

Les requêtes mutatives (PUT, POST, PATCH, DELETE) devraient être systématiquement loggées avec :

  • timestamp,
  • utilisateur authentifié,
  • endpoint appelé.

Rotation des clés JWT

Un mécanisme périodique (mensuel / trimestriel) est recommandé pour prévenir :

  • la fuite éventuelle d'une clé,
  • la persistance de tokens anciens signés avec une clé obsolète.
⚠️ **GitHub.com Fallback** ⚠️