Aller au contenu

PD-19 — Configurer CORS et Security Headers

1. Objectif

Définir un contrat de sécurité d'exposition HTTP, applicable à l'ensemble du backend ProbatioVault, pour : - restreindre les contextes d'appel autorisés ; - neutraliser les interprétations client non prévues des réponses ; - limiter les usages excessifs, y compris sur requêtes formellement valides ; - garantir un comportement déterministe, auditable et configurable par environnement sans modification du code.

2. Périmètre / Hors périmètre

Inclus

  • Tous les endpoints HTTP/HTTPS exposés par le backend en environnements hors réseau interne strict.
  • Tous les clients HTTP (navigateurs, WebView, scripts, applications mobiles, intégrations tierces).
  • Les règles CORS, les security headers de réponse, la limitation de fréquence des requêtes.
  • La différenciation des règles par environnement (dev, test, prod) via configuration externe.
  • La capacité d'audit de la configuration active (valeurs effectives traçables).

Exclu

  • Protection DDoS réseau volumétrique (L3/L4/L7 à grande échelle).
  • Authentification, autorisation, gestion d'identité, gestion de clés, sécurité cryptographique métier.
  • Détection d'attaques sophistiquées ciblées.
  • Garanties de sécurité côté client hors contrôle serveur (poste compromis, navigateur altéré).
  • Toute garantie d'intégrité probatoire de bout en bout non vérifiable uniquement par l'exposition HTTP (hors périmètre car non testable dans cette story).
  • Les exigences eIDAS de valeur probatoire (signature, horodatage qualifié, conservation probatoire) qui sont couvertes par d'autres stories de l'écosystème ProbatioVault.

3. Définitions

  • Origine : triplet {schéma, hôte, port} d'un client Web.
  • Requête cross-origin : requête émise depuis une origine différente de celle de la ressource.
  • Pré-vol (preflight) : requête OPTIONS CORS préalable.
  • Security headers : en-têtes HTTP de durcissement de l'interprétation côté client.
  • Usage excessif : volume/fréquence dépassant un seuil de limitation défini en configuration.
  • Fenêtre de limitation : intervalle temporel de mesure des requêtes.
  • Environnement : contexte d'exécution (dev, test, prod) appliquant une politique dédiée.
  • Comportement déterministe : même entrée de politique + même requête => même décision observable.
  • Auditabilité : capacité à reconstituer la politique et les décisions à partir des traces et de la configuration.
  • Contexte nécessitant CORS : toute requête HTTP contenant l'en-tête Origin (y compris preflight OPTIONS).

4. Invariants (non négociables)

ID Règle Justification
INV-01 Toute réponse HTTP exposée inclut un socle minimal de security headers défini et constant par environnement : X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Content-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none', Referrer-Policy: no-referrer, Permissions-Policy: geolocation=(), camera=(), microphone=(), payment=(), usb=(). Strict-Transport-Security est obligatoire en prod et test (max-age=31536000; includeSubDomains), et en dev uniquement lorsque l'exposition est en HTTPS (max-age=300). Cache-Control: no-store est obligatoire pour les réponses sensibles (authentification, données personnelles, statuts 401/403). Réduire les interprétations dangereuses par défaut.
INV-02 Les en-têtes d'identification d'infrastructure (Server, X-Powered-By ou équivalent) ne doivent jamais divulguer framework/serveur applicatif. Réduire l'information disclosure (RGPD/conformité).
INV-03 Toute décision CORS est fondée exclusivement sur une politique d'origines autorisées configurable par environnement, sans dépendance métier. Paramètres contractuels : méthodes autorisées GET, POST, PUT, PATCH, DELETE, OPTIONS ; en-têtes autorisés Authorization, Content-Type, Accept, Origin, X-Requested-With, X-Correlation-Id ; en-têtes exposés X-Correlation-Id, Retry-After ; Access-Control-Allow-Credentials: true. Conformité ENF-1/ENF-4.
INV-04 Une origine non autorisée ne doit jamais obtenir de décision CORS positive. Origines autorisées : prod = https://app.probatiovault.com, https://www.probatiovault.com ; test = https://app-test.probatiovault.com, https://site-test.probatiovault.com ; dev = http://localhost:3000, http://localhost:8080. Encadrement strict des contextes d'appel.
INV-05 Les requêtes de pré-vol reçoivent une réponse déterministe conforme à la politique active (autorisation/refus explicite), avec cache preflight Access-Control-Max-Age fixé à 600 (prod), 300 (test), 60 (dev). Prévisibilité et testabilité du comportement navigateur.
INV-06 Une politique de limitation de fréquence s'applique à tous les endpoints HTTP exposés, avec seuils configurables par environnement. Valeur par défaut : 100 requêtes / 60 secondes / IP client. Clé de comptage : IP client résolue via chaîne de proxy de confiance, sinon adresse source directe. Contenir les abus triviaux de volume/fréquence.
INV-07 Tout dépassement de seuil produit une réponse de limitation explicite et uniforme : statut 429, en-tête Retry-After (secondes restantes dans la fenêtre), corps JSON {"error":"rate_limited","message":"Too many requests","retryAfter":<seconds>}. Pour endpoints sensibles (ex. authentification), un seuil spécifique plus restrictif peut être défini par configuration sans retirer le seuil global. Comportement déterministe et exploitable par clients.
INV-08 Les mécanismes CORS/headers/limitation ne dépendent ni du rôle utilisateur, ni du contenu métier, ni du type de donnée. Indépendance vis-à-vis du métier.
INV-09 La politique active (origines, paramètres CORS, seuils, fenêtres) est documentée et vérifiable sans lecture du code, et ses décisions sont journalisées en format JSON structuré avec horodatage UTC ISO-8601, identifiant de corrélation, environnement, décision CORS/limitation, IP client, endpoint, statut ; rétention minimale des journaux : 90 jours. Auditabilité opérationnelle.
INV-10 En dev, une configuration locale dédiée permet le développement front-end sans blocage, tout en restant explicitement bornée et traçable : seules http://localhost:3000 et http://localhost:8080 sont autorisées ; les exigences non négociables restent actives (X-Content-Type-Options, X-Frame-Options, Content-Security-Policy, Referrer-Policy, Permissions-Policy, masquage infrastructure, limitation de fréquence). Équilibre sécurité / productivité.
INV-11 Toute réponse d'erreur produite par ces mécanismes conserve les security headers applicables. Durcissement transversal, y compris en erreur.
INV-12 Les règles définies par cette spécification sont exclusivement évaluées au niveau HTTP ; les garanties non observables à ce niveau sont hors périmètre. Frontière contractuelle testable.

5. Flux nominaux

  1. Flux F1 — Requête simple depuis un contexte autorisé
  2. Le client émet une requête HTTP.
  3. Le backend applique la politique CORS de l'environnement si Origin est présent.
  4. Si l'origine est autorisée, la réponse expose les éléments CORS attendus.
  5. La réponse inclut le socle de security headers.
  6. La requête est traitée sous contrainte de limitation active.

  7. Flux F2 — Pré-vol CORS (OPTIONS) depuis un contexte autorisé

  8. Le client envoie un preflight avec origine, méthode cible et en-têtes demandés.
  9. Le backend évalue la politique active.
  10. Si conforme, il répond positivement avec paramètres CORS cohérents.
  11. La réponse est déterministe et n'active aucune logique métier.

  12. Flux F3 — Requête depuis un contexte non autorisé

  13. Le client émet une requête cross-origin depuis une origine non listée.
  14. Le backend refuse la décision CORS positive.
  15. Les indicateurs d'autorisation CORS ne sont pas fournis.
  16. La réponse demeure durcie via security headers.

  17. Flux F4 — Usage sous seuil

  18. Les requêtes restent en deçà des limites configurées.
  19. Le backend répond normalement.
  20. Les traces de contrôle sont disponibles pour audit (au minimum événement de décision).

  21. Flux F5 — Changement d'environnement

  22. Le backend démarre avec la configuration dev/test/prod.
  23. Les politiques CORS/headers/limitation applicables changent sans modification de code.
  24. Les valeurs actives sont consultables dans la documentation d'exploitation.

5bis. Diagrammes Mermaid

Diagramme de séquence — Flux CORS (F1/F2/F3)

Illustre le traitement d'une requête cross-origin par le middleware CORS, couvrant les flux F1 (requête simple autorisée), F2 (preflight autorisé) et F3 (origine non autorisée). Référence : INV-03 (politique CORS), INV-04 (origines autorisées), INV-05 (preflight déterministe), INV-01 (security headers).

sequenceDiagram
    participant Client
    participant CORSMiddleware as CORS Middleware
    participant HeadersMiddleware as Security Headers Middleware
    participant Backend as Backend (route)

    Note over Client,Backend: F2 — Preflight CORS (OPTIONS)
    Client->>CORSMiddleware: OPTIONS /api/resource<br/>Origin: https://app.probatiovault.com<br/>Access-Control-Request-Method: POST
    CORSMiddleware->>CORSMiddleware: Évalue Origin vs politique env<br/>(INV-03, INV-04)
    alt Origine autorisée (INV-04)
        CORSMiddleware-->>Client: 204 No Content<br/>Access-Control-Allow-Origin: https://app.probatiovault.com<br/>Access-Control-Allow-Methods: GET,POST,PUT,PATCH,DELETE,OPTIONS<br/>Access-Control-Allow-Credentials: true<br/>Access-Control-Max-Age: 600 (INV-05)
    else Origine non autorisée (F3)
        CORSMiddleware-->>Client: 204 No Content<br/>Aucun header CORS positif (INV-04)
    end

    Note over Client,Backend: F1 — Requête simple cross-origin
    Client->>CORSMiddleware: POST /api/resource<br/>Origin: https://app.probatiovault.com
    CORSMiddleware->>CORSMiddleware: Évalue Origin vs politique env
    alt Origine autorisée
        CORSMiddleware->>HeadersMiddleware: Forward request
        HeadersMiddleware->>Backend: Forward request
        Backend-->>HeadersMiddleware: 200 OK (réponse métier)
        HeadersMiddleware-->>CORSMiddleware: Ajoute security headers (INV-01)
        CORSMiddleware-->>Client: 200 OK<br/>Access-Control-Allow-Origin: https://app.probatiovault.com<br/>Access-Control-Expose-Headers: X-Correlation-Id, Retry-After<br/>+ Security Headers (INV-01, INV-02)
    else Origine non autorisée (F3)
        CORSMiddleware->>HeadersMiddleware: Forward request
        HeadersMiddleware->>Backend: Forward request
        Backend-->>HeadersMiddleware: 200 OK
        HeadersMiddleware-->>CORSMiddleware: Ajoute security headers (INV-01)
        CORSMiddleware-->>Client: 200 OK<br/>Aucun header CORS positif<br/>+ Security Headers (INV-01, INV-02)
    end

Diagramme d'état — Limitation de fréquence (Rate Limiting)

Modélise les états d'un client IP vis-à-vis du mécanisme de limitation de fréquence. Référence : INV-06 (seuils par environnement), INV-07 (réponse 429 + Retry-After), INV-11 (headers en erreur).

stateDiagram-v2
    [*] --> Nominal : Première requête IP

    Nominal : Sous le seuil (INV-06)
    Nominal : Compteur < S dans fenêtre W
    Nominal : Réponse normale + Security Headers

    Approche : Proche du seuil
    Approche : Compteur ≈ S dans fenêtre W
    Approche : Réponse normale + Security Headers

    Limité : Seuil dépassé (INV-07)
    Limité : Réponse 429 Too Many Requests
    Limité : Retry-After + Corps JSON (INV-07)
    Limité : Security Headers maintenus (INV-11)

    Nominal --> Approche : compteur croît (< S)
    Approche --> Limité : compteur ≥ S (INV-06)
    Limité --> Limité : requête pendant fenêtre active
    Limité --> Nominal : fenêtre W expirée / compteur réinitialisé
    Approche --> Nominal : fenêtre W expirée / compteur réinitialisé

Diagramme d'état — Démarrage et configuration par environnement (F5)

Modélise le comportement au démarrage selon la configuration présente. Référence : INV-10 (dev borné), INV-04 (origines par env), ERR-05 (fail-fast).

stateDiagram-v2
    [*] --> Chargement : Démarrage backend

    Chargement : Lecture config externe
    Chargement : Résolution environnement (dev/test/prod)

    state Chargement <<choice>>
    Chargement --> ConfigValide : Config présente et complète
    Chargement --> ÉchecDémarrage : Config manquante/invalide (ERR-05)

    ConfigValide : Politique CORS + Headers + Rate Limit
    ConfigValide : Origines = f(env) (INV-04)
    ConfigValide : HSTS = f(env) (INV-01)
    ConfigValide : Preflight Max-Age = f(env) (INV-05)
    ConfigValide : Rate Limit seuils = f(env) (INV-06)

    ÉchecDémarrage : Fail-fast explicite (ERR-05)
    ÉchecDémarrage : Pas de mode permissif implicite

    ConfigValide --> Opérationnel : Middleware initialisés
    ÉchecDémarrage --> [*] : Arrêt immédiat

    Opérationnel : Traitement requêtes HTTP
    Opérationnel : Politique active auditable (INV-09)

6. Cas d'erreur

  • ERR-01 — Origine absente/invalide dans un contexte nécessitant CORS
  • S'applique à toute requête portant un en-tête Origin.
  • Réponse conforme à la politique de refus.
  • Aucun indicateur CORS positif.

  • ERR-02 — Origine non autorisée

  • Requête non éligible au partage cross-origin.
  • Observables CORS de refus conformes à la politique active.

  • ERR-03 — Pré-vol incohérent (méthode ou en-têtes non autorisés)

  • Décision CORS négative déterministe.
  • Pas d'accès cross-origin autorisé.

  • ERR-04 — Dépassement de seuil de limitation

  • Réponse de limitation explicite.
  • Statut attendu : 429 Too Many Requests.
  • En-tête Retry-After obligatoire.
  • Corps JSON conforme au contrat : {"error":"rate_limited","message":"Too many requests","retryAfter":<seconds>}.

  • ERR-05 — Configuration manquante ou invalide au démarrage

  • Le service ne doit pas démarrer en mode permissif implicite.
  • Comportement attendu unique : échec explicite de démarrage (fail-fast).

  • ERR-06 — Endpoint non métier (404/405/500)

  • Les security headers restent présents.
  • Les informations d'infrastructure ne sont pas divulguées.

7. Critères d'acceptation (testables)

ID Critère Observable
CA-01 Le socle de security headers est présent sur toute réponse HTTP (succès et erreur). Vérification exhaustive de toute réponse HTTP via middleware global de contrôle des en-têtes (tous statuts et toutes routes exposées).
CA-02 Aucune réponse ne divulgue NestJS, Express ou technologie serveur via Server/X-Powered-By. Inspection systématique des en-têtes de réponse.
CA-03 Une origine autorisée reçoit une décision CORS positive conforme à la politique. Requête avec Origin autorisée + vérification en-têtes CORS.
CA-04 Une origine non autorisée ne reçoit jamais de décision CORS positive. Requête avec Origin non autorisée + absence des en-têtes d'autorisation attendus.
CA-05 Les preflights conformes reçoivent une réponse positive déterministe. Requêtes OPTIONS répétées, mêmes entrées => mêmes sorties CORS.
CA-06 Les preflights non conformes sont refusés de manière déterministe. OPTIONS avec méthode/en-têtes non permis => refus observable.
CA-07 La limitation s'applique transversalement à tous les endpoints exposés. Campagne de tests sur plusieurs routes, y compris sensibles et non sensibles.
CA-08 Au-delà du seuil configuré, la réponse est 429 avec signal de limitation documenté. Test de charge contrôlé dépassant le seuil.
CA-09 Sous le seuil, aucune limitation indue n'est observée. Test de charge contrôlé en dessous des seuils.
CA-10 Les politiques varient par environnement sans modification de code. Démarrage en dev/test/prod + vérification différentiels de comportement documentés.
CA-11 La configuration active est auditable (origines, seuils, fenêtres, paramètres CORS) et les décisions sont traçables. Présence d'une documentation d'exploitation à jour + journaux JSON UTC corrélables avec rétention minimale définie.
CA-12 Le mode dev autorise le travail local front-end selon politique dédiée et bornée. Test depuis origine locale prévue + refus des origines non prévues en dev.
CA-13 Les mécanismes ne dépendent pas de l'identité utilisateur ni de la logique métier. Même requête technique, utilisateurs différents => décision identique CORS/limitation.
CA-14 En cas d'erreur 404, 405 ou 500, les security headers restent appliqués. Génération contrôlée de 404, 405, 500 + inspection des en-têtes.

8. Scénarios de test (Given / When / Then)

ST TC correspondant Critère
ST-01 TC-01 CA-03
ST-02 TC-02 CA-04
ST-03 TC-03 CA-05
ST-04 TC-04 CA-06
ST-05 TC-05 CA-01, CA-14
ST-06 TC-06 CA-02
ST-07 TC-07 CA-08
ST-08 TC-08 CA-09
ST-09 TC-09 CA-10
ST-10 TC-10 CA-12
ST-11 TC-11 CA-13
ST-12 TC-12 CA-11
  • ST-01 (CA-03) : Given une origine listée comme autorisée en environnement cible / When cette origine appelle un endpoint exposé / Then la réponse contient les indicateurs CORS positifs conformes à la politique
  • ST-02 (CA-04) : Given une origine absente de la liste autorisée / When cette origine appelle le même endpoint / Then aucun indicateur CORS positif n'est présent
  • ST-03 (CA-05) : Given un preflight OPTIONS conforme (méthode/en-têtes autorisés) / When la requête est répétée à l'identique / Then les réponses CORS sont identiques et positives
  • ST-04 (CA-06) : Given un preflight OPTIONS demandant une méthode non autorisée / When la requête est envoyée / Then la décision CORS est négative et déterministe
  • ST-05 (CA-01, CA-14) : Given un endpoint renvoyant respectivement 200, 404 et 500 / When chaque endpoint est invoqué / Then le socle de security headers est présent dans tous les cas
  • ST-06 (CA-02) : Given une batterie de requêtes sur plusieurs routes / When les en-têtes de réponse sont inspectés / Then aucune divulgation de framework/serveur n'apparaît
  • ST-07 (CA-08) : Given un seuil de limitation S sur une fenêtre W / When un client envoie S+1 requêtes dans W / Then au moins une réponse est 429 avec signal de limitation
  • ST-08 (CA-09) : Given le même seuil S et fenêtre W / When un client envoie S requêtes ou moins dans W / Then aucune réponse 429 n'est observée
  • ST-09 (CA-10) : Given trois démarrages en dev, test, prod / When les mêmes scénarios CORS/limitation sont exécutés / Then les comportements suivent les politiques documentées de chaque environnement
  • ST-10 (CA-12) : Given l'environnement dev et une origine locale prévue / When le front local appelle l'API / Then l'accès CORS est autorisé selon la politique dev
  • ST-11 (CA-13) : Given deux utilisateurs aux rôles différents et une même requête technique / When ils invoquent le même endpoint / Then la décision CORS/limitation est identique
  • ST-12 (CA-11) : Given la documentation d'exploitation de la politique active / When les valeurs documentées sont comparées au comportement observé / Then la correspondance est vérifiée sans ambiguïté

9. Hypothèses explicites

ID Hypothèse Impact si faux
H-01 Les environnements disposent chacun d'une source de configuration externe fiable. Impossibilité de satisfaire ENF-4 (variation sans code).
H-02 Les clients légitimes Web disposent d'origines stables et identifiables. CORS impossible à sécuriser finement, risque de sur-ou sous-ouverture.
H-03 Les applications mobiles natives n'exigent pas d'exception CORS spécifique hors usages WebView documentés. Risque de faux blocages ou de règles inconsistantes.
H-04 Les seuils de limitation peuvent être définis par l'équipe produit/sécurité. Sans seuil validé, CA-07/08/09 non validables.
H-05 Un mécanisme de journalisation des décisions de sécurité est disponible. Auditabilité partielle, CA-11 fragilisé.
H-06 Les intégrations B2B peuvent être représentées par des règles d'origine ou canal HTTP explicitement défini. Blocage des partenaires ou ouverture excessive.
H-07 Les obligations eIDAS de valeur probatoire sont hors périmètre de cette story et traitées par des stories dédiées ; cette spécification couvre uniquement des observables HTTP. Si un besoin eIDAS est imposé dans ce périmètre, la story doit être re-scopée.

10. Points à clarifier

  1. Exceptions contrôlées — Existence d'exemptions partenaires (B2B), critères, gouvernance et audit des exemptions.
  2. Exigences d'alerting — Logs seuls ou métriques/alertes obligatoires lors des déclenchements de limitation.

Références

  • Epic : BACKEND-CORE (PD-186)
  • JIRA : PD-19
  • Repos concernés : ProbatioVault-backend
  • Documents associés : PD-19-besoin.md

Document produit par le workflow de gouvernance IA — Étape 1 (corrigé après Gate 3 v1) Date : 2026-02-09