- 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.
11 KiB
11 KiB
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
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) :
- Init Vault : génération 5 unseal keys + root token (Shamir secret sharing)
- Unseal : 3 clés parmi 5 requises pour déverrouiller Vault
- 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)
- 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/aesavec 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_encrypteden 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
limiteravec 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)
- Deploy Vault (Docker single-node)
- Migrer secrets .env → Vault
- Encryption emails (AES-256-GCM)
- HTTPS Let's Encrypt (api.roadwave.fr)
- Rate limiting Fiber (100 req/min global)
Phase 2 (Post-MVP - Sprint 6-8)
- Vault HA (3 nodes Raft)
- Rotation automatique credentials
- Field-level encryption GPS (après 24h)
- WAF (Web Application Firewall) : ModSecurity
- Penetration testing externe (Bug Bounty)
Références
- ADR-008 : Authentification (Zitadel, JWT)
- ADR-011 : Accès données (sqlc, prepared statements)
- ADR-015 : Hébergement (OVH France, souveraineté)
- ADR-024 : Monitoring (Audit logs)
- Règle 02 : Conformité RGPD
- HashiCorp Vault Documentation
- OWASP Top 10 2021
- NIST SP 800-175B (Cryptography)