corentin_wakdo/.env.example
Imugiii 5a4897921e
Some checks failed
CI / php-lint (push) Successful in 19s
CI / secret-scan (pull_request) Successful in 8s
CI / secret-scan (push) Successful in 10s
CI / static-tests (push) Successful in 30s
CI / php-lint (pull_request) Successful in 19s
CI / auto-merge (push) Has been skipped
CI / static-tests (pull_request) Successful in 30s
CI / auto-merge (pull_request) Failing after 4s
feat(admin): throttle du PIN d'action sensible par acteur (RG-T22)
Ferme le finding HIGH de la revue Produits (#17) : le PIN d'action sensible
etait verifie sans limitation de tentatives. Conception via panel multi-agents
(3 lentilles + synthese + passe adversariale, holds=true) puis revue de
l'implementation (holds=true).

Dimension du throttle = UTILISATEUR AGISSANT (identite de session, RG-T02), pas
l'email cible (contournable par rotation) ni l'IP (collateral sur poste partage).
Table dediee pin_throttle (entite 22) STRICTEMENT SEPAREE des compteurs de login
(user.failed_login_attempts / login_throttle) : un echec de PIN n'incremente aucun
compteur de connexion (pas d'escalade DoS vers le login).

- db/migrations/0002_pin_throttle.sql : table cle sur actor_user_id (UNIQUE, FK
  -> user ON DELETE CASCADE), separee du login. Appliquee a la base dev.
- ThrottlePolicy : dimension 'pin' (bornes propres PIN_THROTTLE_*, 30s..300s, plus
  permissives que le login : controle de dissuasion, residuel Faible).
- PinThrottle (nouveau) : isLocked / recordFailure (upsert atomique + backoff, une
  transaction, miroir d'AuthService) / reset (UPDATE simple). N'ecrit jamais
  user/login_throttle/audit_log.
- PinVerifier::payTimingDecoy : parite de timing du chemin verrouille.
- ProductController update/destroy : gate AVANT verification (leurre + 422
  generique, pas de pin.failed sous verrou actif = borne anti-flood de l'audit) ;
  recordFailure sur PIN faux ; reset sur succes, cle sur l'acteur de SESSION.
- Docs Merise 21 -> 22 entites : RG-T22 (mlt), entite 22 pin_throttle
  (mcd/mld/dictionary), couverture MCT 22/22 (mct).
- .env.example + docker-compose : PIN_THROTTLE_THRESHOLD/BASE/MAX/WINDOW.
- Journal RNCP : docs/journal/2026-06-15--p3-throttle-pin-rg-t22.md.

Tests : 188 verts (525 assertions), PHPStan L6 propre.
2026-06-15 22:03:07 +00:00

165 lines
7.4 KiB
Text

#
# Wakdo - template de configuration
#
# Usage :
# cp .env.example .env
# Editer .env (gitignore) avec les valeurs reelles.
#
# Audience :
# Destine a l'auteur, au jury et aux contributeurs futurs.
#
# Modele de deploiement :
# Ce projet tourne sur serveur derriere un reverse proxy Traefik. Il n'y a
# pas de binding de ports hote : l'acces se fait uniquement via les FQDN
# configures ci-dessous et routes par Traefik (reseau admin_proxy).
# Les distinctions dev / staging / prod se font par FQDN distincts
# (ex : .dev.acadenice.fr vs .acadenice.fr) et par .env dedie.
#
# ===================================================================
# Environnement applicatif
# ===================================================================
APP_ENV=dev # dev | staging | prod
APP_DEBUG=true # true en dev, false en prod
APP_TIMEZONE=Europe/Paris
# URL publique de la borne (Bloc 1), doit pointer vers le FQDN Traefik.
# Placeholder example.com (RFC 2606) - a remplacer par le FQDN reel.
APP_URL_KIOSK=https://kiosk.example.com
# URL publique du back-office + API (Bloc 2).
# Placeholder example.com (RFC 2606) - a remplacer par le FQDN reel.
APP_URL_ADMIN=https://admin.example.com
# ===================================================================
# Base de donnees (MariaDB)
# ===================================================================
# Valeurs ci-dessous = PLACEHOLDERS. Remplacer par des mots de passe forts.
# Pas accessible depuis l'exterieur : le service wakdo-db est sur le reseau
# interne uniquement, aucun port exposé a l'hote.
DB_HOST=wakdo-db # nom du service docker-compose
DB_PORT=3306
DB_NAME=wakdo
DB_USER=wakdo
DB_PASSWORD=change_me_strong_password
DB_ROOT_PASSWORD=change_me_root_password
# ===================================================================
# Sessions
# ===================================================================
SESSION_LIFETIME_IDLE=14400 # 4h en secondes - idle timeout
SESSION_LIFETIME_ABSOLUTE=36000 # 10h en secondes - absolute timeout
SESSION_NAME=WAKDO_SID # nom du cookie (evite PHPSESSID)
# ===================================================================
# Securite
# ===================================================================
# Origine autorisee pour les requetes CORS de l'API.
# Doit correspondre exactement a APP_URL_KIOSK (pas de wildcard).
CORS_ALLOWED_ORIGIN=https://kiosk.example.com
# Algorithme de hashage mot de passe (password_hash PHP).
# argon2id recommande depuis PHP 7.3 pour les nouveaux projets.
PASSWORD_ALGO=argon2id
# Parametres de cout argon2id (password_hash options).
# Defauts alignes sur les recommandations OWASP Password Storage Cheat Sheet
# (memoire >= 19 MiB, >= 2 iterations). 65536 KiB = 64 MiB, marge confortable.
# Ces valeurs servent aussi au hash du PIN equipier (pin_hash, actions sensibles).
ARGON2_MEMORY_COST=65536 # KiB (64 MiB)
ARGON2_TIME_COST=4 # nombre d'iterations
ARGON2_THREADS=1 # parallelisme (1 = portable, deterministe)
# ===================================================================
# Anti brute-force - throttling de connexion (security-by-design)
# ===================================================================
# Deux gardes complementaires (cf. docs/merise/mld.md 4.21 + PROJECT_CONTEXT 19) :
# 1. par compte : colonnes user.failed_login_attempts / user.lockout_until
# 2. par IP source : table login_throttle (entite 21)
# Backoff degressif (pas de lock definitif) : evite le DoS sur compte legitime.
# Compteur par compte : nombre d'echecs avant declenchement du backoff.
ACCOUNT_LOCKOUT_THRESHOLD=5
# Duree de base du backoff (secondes). Croit de facon degressive a chaque
# palier d'echecs supplementaires (ex : 60 -> 300 -> 900).
ACCOUNT_LOCKOUT_BASE_SECONDS=60
ACCOUNT_LOCKOUT_MAX_SECONDS=900 # plafond du backoff (15 min)
# Gate par IP : fenetre glissante et plafond de tentatives sur cette fenetre.
IP_THROTTLE_WINDOW_SECONDS=900 # 15 min
IP_THROTTLE_MAX_ATTEMPTS=20 # par IP sur la fenetre
# PIN equipier pour actions sensibles (annulation, override). Chiffres uniquement,
# bornes min ET max (RG-T18 : validation serveur + longueur bornee).
STAFF_PIN_MIN_LENGTH=4
STAFF_PIN_MAX_LENGTH=12
# Throttle du PIN d'action sensible (RG-T22) - compteurs SEPARES du login : la
# dimension est l'utilisateur AGISSANT (session), pas l'email cible ni l'IP. Bornes
# volontairement plus permissives que le login (controle de dissuasion) : ne pas
# bloquer un manager en plein rush sur quelques fautes de frappe.
PIN_THROTTLE_THRESHOLD=5 # echecs avant le backoff (par acteur)
PIN_THROTTLE_BASE_SECONDS=30 # 1er palier (vs 60 au login)
PIN_THROTTLE_MAX_SECONDS=300 # plafond du backoff (5 min, vs 900 au login)
PIN_THROTTLE_WINDOW_SECONDS=900 # fenetre glissante (15 min)
# Expiration du token de reinitialisation de mot de passe (secondes).
PASSWORD_RESET_TTL=3600 # 1h
# ===================================================================
# Retention des donnees (RGPD - minimisation et limitation de conservation)
# ===================================================================
# Purges executees par le service cron (docker/cron/crontab).
# Justification documentee : interet legitime / obligations probatoires.
AUDIT_LOG_RETENTION_DAYS=365 # journal d'audit ~12 mois
THROTTLE_PURGE_AFTER_HOURS=24 # login_throttle : lignes sans lockout actif > 24h
ORDER_RETENTION_DAYS=1095 # commandes (historique/stats) ~3 ans
# Purge des sessions expirees : deja geree par le cron */15 (voir crontab).
# ===================================================================
# Upload images produits
# ===================================================================
UPLOAD_MAX_SIZE_MB=5
UPLOAD_ALLOWED_MIME=image/jpeg,image/png,image/webp
# ===================================================================
# Cron (fenetre de maintenance 01h30 - 09h30)
# ===================================================================
# Les jobs sont definis dans docker/cron/crontab. Ici uniquement le TZ.
CRON_TIMEZONE=Europe/Paris
# ===================================================================
# Exposition via Traefik
# ===================================================================
# FQDN consommes par les labels docker-compose.yml pour generer les routes
# Traefik et les certificats TLS (Traefik gere le resolver par defaut).
# Le Traefik de l'hote prend en charge Let's Encrypt automatiquement.
TRAEFIK_DOMAIN_KIOSK=kiosk.example.com
TRAEFIK_DOMAIN_ADMIN=admin.example.com
# ===================================================================
# Reseau Docker externe du reverse proxy
# ===================================================================
# Nom du reseau Docker externe auquel le service web doit se connecter
# pour etre expose par le reverse proxy de l'hote.
#
# Adapter selon votre infrastructure. Valeurs courantes :
# traefik_proxy - convention neutre (placeholder)
# traefik_public - convention doc Traefik
# traefik - setups simples
# proxy - autre convention frequente
#
# Le reseau doit exister AVANT 'make init' (cree par votre stack de
# reverse proxy, ou manuellement : docker network create <nom>).
# La cible 'make init' echoue proprement avec un message d'aide si le
# reseau est introuvable.
REVERSE_PROXY_NETWORK=traefik_proxy