diff --git a/docs/ENVIRONMENT_VARIABLES.md b/docs/ENVIRONMENT_VARIABLES.md new file mode 100644 index 0000000..02f5016 --- /dev/null +++ b/docs/ENVIRONMENT_VARIABLES.md @@ -0,0 +1,92 @@ +# Variables d'environnement — Adastra API + +Référence pour les variables sensibles ou non-triviales. Pour les valeurs par défaut et la liste exhaustive, voir `config/env/.env.example`. + +--- + +## TRUST_PROXY + +**Fichier concerné :** `src/createApp.js` +**Utilisé par :** `express-rate-limit` (IP réelle du client), `req.ip` + +### Principe + +Indique à Express combien de proxies se trouvent entre internet et le process Node. Sans cette valeur, `req.ip` retourne l'IP du proxy (`127.0.0.1`) au lieu de l'IP du client réel, ce qui rend le rate limiting inefficace (toutes les requêtes semblent venir de la même IP). + +### Valeurs selon la topologie + +| Environnement | Topologie | Valeur | +|---|---|---| +| Dev local | Node exposé directement, pas de proxy | ne pas setter (variable absente) | +| Prod — serveur seul | nginx → Node sur le même serveur | `1` | +| Prod — cloud avec LB | nginx → load balancer → Node | `2` | +| Confiance totale | Tous les proxies de la chaîne `X-Forwarded-For` | `true` (déconseillé) | + +> **Règle générale :** `TRUST_PROXY` = nombre de proxies entre internet et Node. + +### Vérification empirique + +Ajouter temporairement cette route et appeler `/debug-ip` depuis l'extérieur : + +```js +app.get('/debug-ip', (req, res) => { + res.json({ + ip: req.ip, + ips: req.ips, + xForwardedFor: req.headers['x-forwarded-for'] + }); +}); +``` + +- `req.ip` retourne l'IP réelle du client → valeur correcte +- `req.ip` retourne `127.0.0.1` ou l'IP du proxy → augmenter de 1 +- `req.ips` est vide malgré un proxy → idem + +**Supprimer la route après vérification.** + +### Implémentation actuelle + +```js +if (process.env.TRUST_PROXY) { + app.set('trust proxy', parseInt(process.env.TRUST_PROXY, 10) || process.env.TRUST_PROXY); +} +``` + +Le cast `parseInt(...) || process.env.TRUST_PROXY` permet de passer soit un entier (`"1"` → `1`) soit la chaîne `"true"`. + +--- + +## SECRET + +**Fichier concerné :** `src/config/index.js` +**Utilisé par :** signature JWT, HMAC-SHA256 des API keys, secret de session Express + +Valeur obligatoire en production — le process refuse de démarrer si elle est absente. Générer avec : + +```bash +node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" +``` + +--- + +## SESSION_SECRET + +**Fichier concerné :** `src/createApp.js` +**Utilisé par :** `express-session` + +Indépendant de `SECRET` pour limiter la surface d'impact si l'un des deux est compromis. Même commande de génération que `SECRET`. Fallback sur `SECRET` si absent (rétrocompatibilité). + +--- + +## ALLOWED_ORIGINS + +**Fichier concerné :** `src/createApp.js` +**Utilisé par :** CORS + +Liste d'origines autorisées, séparées par des virgules. Si absente ou vide, CORS refuse toutes les origines cross-domain. + +``` +ALLOWED_ORIGINS=http://localhost:4200,http://localhost:4300 +``` + +En production, indiquer uniquement le domaine du frontend (ex: `https://adastra.example.com`). diff --git a/package-lock.json b/package-lock.json index 3183273..27a8493 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,10 @@ "errorhandler": "^1.5.1", "express": "^4.18.2", "express-jwt": "^8.4.1", + "express-rate-limit": "^8.4.1", "express-session": "^1.18.0", + "express-validator": "^7.3.2", + "helmet": "^8.1.0", "js-yaml": "^4.1.1", "jsonwebtoken": "^9.0.2", "method-override": "3.0.0", @@ -5005,6 +5008,33 @@ "node": ">= 8.0.0" } }, + "node_modules/express-rate-limit": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.4.1.tgz", + "integrity": "sha512-NGVYwQSAyEQgzxX1iCM978PP9AdO/hW93gMcF6ZwQCm+rFvLsBH6w4xcXWTcliS8La5EPRN3p9wzItqBwJrfNw==", + "license": "MIT", + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express-rate-limit/node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/express-session": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz", @@ -5041,6 +5071,25 @@ "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-2.1.3.tgz", "integrity": "sha512-wj4tLMyCVYuIIKHGt0FhCtIViBcwzWejX0EjNxveAa6dG+0XBCQhMbx+PnkLkFCxLC69qoFrxds4pIyL88inaQ==" }, + "node_modules/express-validator": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-7.3.2.tgz", + "integrity": "sha512-ctLw1Vl6dXVH62dIQMDdTAQkrh480mkFuG6/SGXOaVlwPNukhRAe7EgJIMJ2TSAni8iwHBRp530zAZE5ZPF2IA==", + "license": "MIT", + "dependencies": { + "lodash": "^4.18.1", + "validator": "~13.15.23" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/express-validator/node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, "node_modules/express/node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -5750,6 +5799,15 @@ "node": ">= 0.4" } }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -10450,9 +10508,10 @@ } }, "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "version": "13.15.35", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", + "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", + "license": "MIT", "engines": { "node": ">= 0.10" } diff --git a/package.json b/package.json index 8e87c90..a8d8846 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,10 @@ "errorhandler": "^1.5.1", "express": "^4.18.2", "express-jwt": "^8.4.1", + "express-rate-limit": "^8.4.1", "express-session": "^1.18.0", + "express-validator": "^7.3.2", + "helmet": "^8.1.0", "js-yaml": "^4.1.1", "jsonwebtoken": "^9.0.2", "method-override": "3.0.0",