Files
la-voix-du-peuple/.env.example
T
billisdead defc07648c Security: close the 9 items from the June review + one found along the way
Findings from the 2026-06-24 security review, now mandatory before this
platform leaves the LAN for a public VPS (see budget/externalization note):

- Timing attack on admin password: hmac.compare_digest instead of !=
- CORS wildcard removed, restricted to ALLOWED_ORIGINS (needed for credentials)
- hCaptcha now fails closed on network error instead of open
- HMAC signatures use the full SHA-256 digest instead of a 64-bit truncation
- Consultation-scoped submissions were missing the consent/cooldown checks
  the global submission route already had
- Admin search capped at 200 chars (ILIKE cost)
- Webhook URLs validated against SSRF (private/loopback/link-local/metadata
  IPs rejected, re-checked at trigger time too, redirects not followed)
- Flood tracker backed by Redis when REDIS_URL is set (shared across Gunicorn
  workers, survives restarts), falls back to in-memory otherwise
- Admin session moved from sessionStorage/Bearer token to an httpOnly+Secure+
  SameSite=Strict cookie; new /api/admin/logout and /api/admin/session routes

Also found while wiring ProxyFix for the cookie's Secure flag: the app never
told Flask about the Nginx reverse proxy, so request.remote_addr saw Nginx's
own IP for every request in production — silently breaking rate limiting,
flood detection, and IP blacklist (everyone would collapse onto one shared
key). Added ProxyFix (1 hop).

CSV exports now stream instead of loading everything into memory. AI prompts
delimit citizen-submitted content explicitly and instruct the model to treat
it as inert data, never instructions, to reduce prompt injection surface.

Docs (DEX.md, SECURITE_ANTI_ABUS.md, README.md, DEPLOIEMENT.md) and
dev-local.sh updated to match.
2026-09-10 09:27:50 +02:00

75 lines
4.7 KiB
Bash

# La Voix du Peuple — Variables d'environnement
# Copiez ce fichier en .env et remplissez les valeurs.
# Sécurisez le fichier : chmod 600 .env
# ─── Base de données PostgreSQL ──────────────────────────────────────────────
# Format : postgresql://utilisateur:motdepasse@hote:port/nomdb
DATABASE_URL=postgresql://voixdupeuple:CHANGEME@localhost:5432/voixdupeuple
# ─── IA — Mistral (recommandé — souveraineté européenne) ─────────────────────
# Obtenez votre clé sur https://console.mistral.ai
MISTRAL_API_KEY=sk-...
# ─── IA — Alternative OpenAI-compatible (si Mistral non disponible) ──────────
# OPENAI_API_KEY=sk-...
# OPENAI_BASE_URL=https://votre-proxy.example.com/v1 # Optionnel : proxy/Ollama
# ─── Modèles IA (optionnel — valeurs par défaut ci-dessous) ──────────────────
# FILTER_MODEL=mistral-small-latest # Modèle de filtrage (rapide, économique)
# SYNTHESIS_MODEL=mistral-large-latest # Modèle de synthèse (haute qualité)
# ─── Sécurité Flask ───────────────────────────────────────────────────────────
# Générez avec : python3 -c "import secrets; print(secrets.token_hex(32))"
SECRET_KEY=CHANGEZ_CE_SECRET_AVEC_UNE_VALEUR_ALEATOIRE_LONGUE
# ─── Panel d'administration ───────────────────────────────────────────────────
# Mot de passe pour accéder à /admin
ADMIN_SECRET=CHANGEZ_CE_MOT_DE_PASSE_ADMIN
# ─── Flask ────────────────────────────────────────────────────────────────────
FLASK_ENV=production
PORT=8080
# ─── CORS ─────────────────────────────────────────────────────────────────────
# Origines autorisées à appeler l'API en cross-origin, séparées par des virgules.
# Jamais de wildcard "*" — incompatible avec les cookies de session (admin, RGPD,
# cooldown). Sans cette variable, retombe sur VITE_APP_URL ci-dessous ; si aucune
# des deux n'est définie, aucune origine cross-origin n'est autorisée.
# ALLOWED_ORIGINS=https://votredomaine.fr
# ─── Anti-abus (optionnel — valeurs par défaut raisonnables) ─────────────────
# REDIS_URL=redis://localhost:6379/0 # Rate limiting persistant (recommandé en prod)
# RATE_LIMIT_CONTRIBUTIONS=5 per minute;3 per hour # Limite de soumissions
# CONTRIBUTION_COOLDOWN_SECONDS=3600 # Délai entre deux soumissions (même session)
# FLOOD_THRESHOLD=10 # Alerte flood : nb soumissions / 5 min / IP
# ─── hCaptcha (optionnel — recommandé en production) ─────────────────────────
# Créez un compte sur https://www.hcaptcha.com (RGPD-friendly)
# HCAPTCHA_SECRET_KEY=votre-cle-secrete-hcaptcha
# VITE_HCAPTCHA_SITE_KEY=votre-cle-de-site-hcaptcha # Nécessite rebuild frontend
# ─── API Légifrance — Injection dynamique des textes légaux (optionnel) ───────
# Permet de récupérer les textes des articles légaux en vigueur depuis Légifrance
# et de les injecter dans le prompt de filtrage, garantissant que le modèle IA
# travaille sur les textes officiels consolidés du jour, et non sa mémoire d'entraînement.
#
# Inscription gratuite sur https://piste.gouv.fr
# Créez une application → activez l'API Légifrance → copiez Client ID et Client Secret.
#
# PISTE_CLIENT_ID=votre-client-id
# PISTE_CLIENT_SECRET=votre-client-secret
#
# Durée de cache des textes légaux (Redis, défaut 24h) :
# LEGIFRANCE_CACHE_TTL=86400
#
# Initialisation (première fois, pour résoudre les LEGIARTI IDs) :
# python scripts/check_legal_refs.py --init
# Vérification hebdomadaire des changements :
# python scripts/check_legal_refs.py --check
# Mise à jour du YAML après changement :
# python scripts/check_legal_refs.py --update
# ─── Frontend ─────────────────────────────────────────────────────────────────
# URL publique du site (utilisée par le QR code et les exports)
VITE_APP_URL=https://votredomaine.fr