Files
roadwave/docs/adr/025-securite-secrets.md
jpgiannetti 35aaa105d0 docs: améliorer rendu markdown et navigation mkdocs
- Ajouter ADR-018 (librairies Go) dans TECHNICAL.md
- Transformer Shared en menu dépliable dans mkdocs (cohérence avec autres domaines)
- Corriger listes markdown (ajout lignes vides avant listes)
- Corriger line breaks dans génération BDD (étapes "Et" sur nouvelles lignes)
- Ajouter script fix-markdown-lists.sh pour corrections futures

Impacte 86 fichiers de documentation et 164 fichiers BDD générés.
2026-02-09 20:49:52 +01:00

288 lines
11 KiB
Markdown

# ADR-025 : Sécurité - Secrets Management et Encryption
**Statut** : Accepté
**Date** : 2026-02-01
## Contexte
RoadWave manipule des données sensibles nécessitant une protection renforcée :
- **Secrets applicatifs** : JWT signing key, DB credentials, Mangopay API keys
- **PII utilisateurs** : Positions GPS précises, emails, données bancaires (via Mangopay)
- **Conformité** : RGPD (minimisation données, encryption at rest), PCI-DSS (paiements)
- **Souveraineté** : Self-hosted requis (ADR-015)
Contrainte : **OWASP Top 10 mitigation** obligatoire pour sécurité applicative.
## Décision
Stratégie **secrets management + encryption at rest + HTTPS** avec stack self-hosted.
### Stack Sécurité
| Composant | Technologie | Licence | Justification |
|-----------|-------------|---------|---------------|
| **Secrets management** | HashiCorp Vault (open source) | MPL-2.0 | Standard industrie, rotation auto, audit logs |
| **Encryption PII** | AES-256-GCM (crypto/aes Go) | BSD-3 | NIST approuvé, AEAD (authenticated) |
| **HTTPS/TLS** | Let's Encrypt (Certbot) | ISC | Gratuit, renouvellement auto, wildcard support |
| **CORS/CSRF** | Fiber middleware | MIT | Protection XSS/CSRF intégrée |
| **Rate limiting** | Redis + Token Bucket (Fiber) | MIT/Apache | Protection brute-force, DDoS |
| **SQL injection** | sqlc (prepared statements) | MIT | Parameterized queries (ADR-011) |
### Architecture Secrets
```mermaid
graph TB
subgraph Dev["Environnement Dev"]
EnvFile[".env file<br/>(local uniquement)"]
end
subgraph Prod["Production"]
Vault["HashiCorp Vault<br/>(secrets storage)"]
API["Backend Go API"]
DB["PostgreSQL<br/>(encrypted at rest)"]
Redis["Redis<br/>(TLS enabled)"]
end
subgraph Encryption["Encryption Layer"]
AES["AES-256-GCM<br/>(PII encryption)"]
TLS["TLS 1.3<br/>(transport)"]
end
subgraph Secrets["Secrets Stockés"]
JWT["JWT Signing Key<br/>(RS256 private key)"]
DBCreds["DB Credentials<br/>(user/pass)"]
Mangopay["Mangopay API Key<br/>(sandbox + prod)"]
EncKey["Encryption Master Key<br/>(AES-256)"]
end
EnvFile -.->|dev only| API
Vault --> API
Vault --- JWT
Vault --- DBCreds
Vault --- Mangopay
Vault --- EncKey
API --> AES
API --> TLS
AES --> DB
TLS --> DB
TLS --> Redis
classDef devStyle fill:#fff3e0,stroke:#e65100
classDef prodStyle fill:#e3f2fd,stroke:#1565c0
classDef encStyle fill:#f3e5f5,stroke:#6a1b9a
classDef secretStyle fill:#ffebee,stroke:#c62828
class Dev,EnvFile devStyle
class Prod,Vault,API,DB,Redis prodStyle
class Encryption,AES,TLS encStyle
class Secrets,JWT,DBCreds,Mangopay,EncKey secretStyle
```
### Secrets Management avec Vault
**Initialisation Vault** (one-time setup) :
1. Init Vault : génération 5 unseal keys + root token (Shamir secret sharing)
2. Unseal : 3 clés parmi 5 requises pour déverrouiller Vault
3. Login root + activation KV-v2 engine (path : `roadwave/`)
**Secrets stockés** :
- **JWT signing key** : Paire RS256 privée/publique
- **Database credentials** : Host, port, user, password (généré aléatoire 32 caractères)
- **Mangopay API** : Client ID, API key, webhook secret
**Récupération depuis Backend Go** :
- Utilisation SDK `hashicorp/vault/api`
- Authentification via token Vault (variable env VAULT_TOKEN)
- Récupération secrets via KVv2 engine
### Encryption PII (Field-level)
**Données chiffrées** (AES-256-GCM) :
- **GPS précis** : lat/lon conservés 24h puis réduits à geohash-5 (~5km²) ([Règle 02](../domains/_shared/rules/rgpd.md))
- **Email** : chiffré en base, déchiffré uniquement à l'envoi
- **Numéro téléphone** : si ajouté (Phase 2)
**Architecture encryption** :
- Utilisation bibliothèque standard Go `crypto/aes` avec mode GCM (AEAD)
- Master key 256 bits (32 bytes) récupérée depuis Vault
- Chiffrement : génération nonce aléatoire + seal GCM → encodage base64
- Stockage : colonne `email_encrypted` en base PostgreSQL
**Contraintes** :
- Index direct sur champ chiffré impossible
- Solution : index sur hash SHA-256 de l'email chiffré pour recherche
### HTTPS/TLS Configuration
**Let's Encrypt wildcard certificate** :
- Méthode : Certbot avec DNS-01 challenge (API OVH)
- Domaines couverts : `roadwave.fr` + `*.roadwave.fr` (wildcard)
- Renouvellement : automatique via cron quotidien (30j avant expiration)
**Nginx TLS configuration** :
- Protocol : TLS 1.3 uniquement (pas de TLS 1.2 ou inférieur)
- Ciphers : TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384
- HSTS : max-age 1 an, includeSubDomains
- Security headers : X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy strict-origin-when-cross-origin
## Alternatives considérées
### Secrets Management
| Option | Coût | Hébergement | Rotation auto | Audit | Verdict |
|--------|------|-------------|---------------|-------|---------|
| **Vault (OSS)** | **0€** | Self-hosted | ✅ Oui | ✅ Oui | ✅ Choisi |
| Vault Enterprise | 150$/mois | Self-hosted | ✅ Oui | ✅ Oui | ❌ Overkill MVP |
| Kubernetes Secrets | 0€ | K8s only | ❌ Non | ⚠️ Limité | ⚠️ Phase 2 (K8s) |
| Variables env (.env) | 0€ | VM/container | ❌ Non | ❌ Non | ❌ Insécure prod |
| AWS Secrets Manager | 0.40$/secret/mois | Cloud AWS | ✅ Oui | ✅ Oui | ❌ Souveraineté |
### Encryption Library
| Option | Performance | AEAD | FIPS 140-2 | Verdict |
|--------|-------------|------|------------|---------|
| **crypto/aes (Go std)** | ⭐⭐⭐ Rapide | ✅ GCM | ✅ Approuvé | ✅ Choisi |
| age (filippo.io/age) | ⭐⭐ Moyen | ✅ ChaCha20 | ❌ Non | ⚠️ Moins standard |
| NaCl/libsodium | ⭐⭐⭐ Rapide | ✅ Poly1305 | ❌ Non | ⚠️ CGO dependency |
### TLS Certificate
| Option | Coût | Renouvellement | Wildcard | Verdict |
|--------|------|----------------|----------|---------|
| **Let's Encrypt** | **0€** | Auto (90j) | ✅ Oui (DNS-01) | ✅ Choisi |
| OVH SSL | 5€/an | Manuel | ✅ Oui | ❌ Coût inutile |
| Cloudflare SSL | 0€ | Auto | ✅ Oui | ⚠️ Proxy Cloudflare |
## Justification
### HashiCorp Vault
- **Standard industrie** : utilisé par 80% Fortune 500
- **Rotation automatique** : credentials DB renouvelés toutes les 90j
- **Audit logs** : qui a accédé à quel secret, quand
- **Unseal ceremony** : sécurité maximale (3/5 clés requises)
- **Coût 0€** : version open source MPL-2.0
### AES-256-GCM
- **NIST approuvé** : standard gouvernement US (FIPS 140-2)
- **AEAD** : Authenticated Encryption with Associated Data (pas de tampering)
- **Performance** : hardware acceleration (AES-NI CPU)
- **Bibliothèque std Go** : pas de dépendance externe
### Let's Encrypt
- **Gratuit** : économie 50-200€/an vs certificat commercial
- **Automatique** : Certbot renouvelle 30j avant expiration
- **Wildcard** : 1 certificat pour *.roadwave.fr (tous sous-domaines)
- **Adopté massivement** : 300M+ sites web
## Conséquences
### Positives
-**Conformité RGPD** : encryption at rest PII, minimisation données
-**PCI-DSS** : secrets paiement isolés (Mangopay API key dans Vault)
-**OWASP Top 10** : SQL injection (sqlc), XSS/CSRF (Fiber), rate limiting
-**Coût 0€** : stack complète open source
-**Audit trail** : logs Vault tracent tous accès secrets
### Négatives
- ⚠️ **Vault unseal** : nécessite 3/5 clés au redémarrage serveur (procédure manuelle)
- ⚠️ **Performance encryption** : +0.5-2ms latency par champ chiffré (acceptable)
-**Complexité opérationnelle** : Vault à maintenir (backups, upgrades)
-**Recherche email impossible** : chiffrement empêche `WHERE email = 'x'` (utiliser hash)
### OWASP Top 10 Mitigation
| Vulnérabilité | Mitigation RoadWave | Implémentation |
|---------------|---------------------|----------------|
| **A01: Broken Access Control** | JWT scopes + RBAC | Zitadel roles (ADR-008) |
| **A02: Cryptographic Failures** | AES-256-GCM + TLS 1.3 | crypto/aes + Let's Encrypt |
| **A03: Injection** | Prepared statements (sqlc) | ADR-011 |
| **A04: Insecure Design** | Threat modeling + ADR reviews | Process architecture |
| **A05: Security Misconfiguration** | Vault secrets + hardened config | ADR-025 |
| **A06: Vulnerable Components** | Dependabot + go mod tidy | GitHub Actions |
| **A07: Auth Failures** | Zitadel + rate limiting | ADR-008 + Fiber middleware |
| **A08: Software Integrity** | Code signing + SBOM | Phase 2 |
| **A09: Logging Failures** | Loki centralisé + audit Vault | ADR-024 |
| **A10: SSRF** | Whitelist URLs + network policies | Fiber middleware |
### Rate Limiting (Protection DDoS/Brute-force)
**Configuration** :
- Middleware Fiber `limiter` avec backend Redis
- Limite : 100 requêtes par minute par IP (global)
- Clé de limitation : adresse IP client
- Réponse limitation : HTTP 429 "Too many requests"
**Rate limits par endpoint** :
- `/auth/login` : 5 req/min/IP (protection brute-force)
- `/moderation/report` : 10 req/24h/user (anti-spam)
- API générale : 100 req/min/IP
## Rotation des Secrets
**Politique de rotation** :
| Secret | Rotation | Justification |
|--------|----------|---------------|
| **JWT signing key** | 1 an | Compromission = invalidation tous tokens |
| **DB credentials** | 90 jours | Best practice NIST |
| **Mangopay API key** | À la demande | Rotation manuelle si compromission |
| **Encryption master key** | Jamais (re-encryption massive) | Backup sécurisé uniquement |
**Process rotation DB credentials** :
- Vault génère automatiquement nouveau password
- Vault met à jour PostgreSQL avec nouveau password
- Application récupère nouveau password au prochain accès Vault
- Ancien password invalide après grace period de 1h
## Métriques de Succès
- 0 fuite secrets en production (audit logs Vault)
- 100% traffic HTTPS (HTTP → HTTPS redirect)
- Rate limiting < 0.1% false positives
- Encryption overhead < 2ms p95
## Migration et Rollout
### Phase 1 (MVP - Sprint 2-3)
1. Deploy Vault (Docker single-node)
2. Migrer secrets .env → Vault
3. Encryption emails (AES-256-GCM)
4. HTTPS Let's Encrypt (api.roadwave.fr)
5. Rate limiting Fiber (100 req/min global)
### Phase 2 (Post-MVP - Sprint 6-8)
1. Vault HA (3 nodes Raft)
2. Rotation automatique credentials
3. Field-level encryption GPS (après 24h)
4. WAF (Web Application Firewall) : ModSecurity
5. Penetration testing externe (Bug Bounty)
## Références
- [ADR-008 : Authentification](008-authentification.md) (Zitadel, JWT)
- [ADR-011 : Accès données](011-orm-acces-donnees.md) (sqlc, prepared statements)
- [ADR-015 : Hébergement](015-hebergement.md) (OVH France, souveraineté)
- [ADR-024 : Monitoring](024-monitoring-observabilite.md) (Audit logs)
- [Règle 02 : Conformité RGPD](../domains/_shared/rules/rgpd.md)
- [HashiCorp Vault Documentation](https://www.vaultproject.io/docs)
- [OWASP Top 10 2021](https://owasp.org/Top10/)
- [NIST SP 800-175B (Cryptography)](https://csrc.nist.gov/publications/detail/sp/800-175b/final)