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
OPTIONSCORS 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 preflightOPTIONS).
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¶
- Flux F1 — Requête simple depuis un contexte autorisé
- Le client émet une requête HTTP.
- Le backend applique la politique CORS de l'environnement si
Originest présent. - Si l'origine est autorisée, la réponse expose les éléments CORS attendus.
- La réponse inclut le socle de security headers.
-
La requête est traitée sous contrainte de limitation active.
-
Flux F2 — Pré-vol CORS (
OPTIONS) depuis un contexte autorisé - Le client envoie un preflight avec origine, méthode cible et en-têtes demandés.
- Le backend évalue la politique active.
- Si conforme, il répond positivement avec paramètres CORS cohérents.
-
La réponse est déterministe et n'active aucune logique métier.
-
Flux F3 — Requête depuis un contexte non autorisé
- Le client émet une requête cross-origin depuis une origine non listée.
- Le backend refuse la décision CORS positive.
- Les indicateurs d'autorisation CORS ne sont pas fournis.
-
La réponse demeure durcie via security headers.
-
Flux F4 — Usage sous seuil
- Les requêtes restent en deçà des limites configurées.
- Le backend répond normalement.
-
Les traces de contrôle sont disponibles pour audit (au minimum événement de décision).
-
Flux F5 — Changement d'environnement
- Le backend démarre avec la configuration
dev/test/prod. - Les politiques CORS/headers/limitation applicables changent sans modification de code.
- 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-Afterobligatoire. -
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
OPTIONSconforme (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
OPTIONSdemandant 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
Ssur une fenêtreW/ When un client envoieS+1requêtes dansW/ Then au moins une réponse est429avec signal de limitation - ST-08 (CA-09) : Given le même seuil
Set fenêtreW/ When un client envoieSrequêtes ou moins dansW/ Then aucune réponse429n'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
devet une origine locale prévue / When le front local appelle l'API / Then l'accès CORS est autorisé selon la politiquedev - 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¶
- Exceptions contrôlées — Existence d'exemptions partenaires (B2B), critères, gouvernance et audit des exemptions.
- 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