Backend Go: - Remplacement complet des anciennes migrations par la base V1 alignée sur ORIGIN. - Durcissement global du parsing JSON (BindAndValidateJSON + RespondWithAppError). - Sécurisation de config.go, CORS, statuts de santé et monitoring. - Implémentation des transactions P0 (RBAC, duplication de playlists, social toggles). - Ajout d’un job worker structuré (emails, analytics, thumbnails) + tests associés. - Nouvelle doc backend : AUDIT_CONFIG, BACKEND_CONFIG, AUTH_PASSWORD_RESET, JOB_WORKER_*. Chat server (Rust): - Refonte du pipeline JWT + sécurité, audit et rate limiting avancé. - Implémentation complète du cycle de message (read receipts, delivered, edit/delete, typing). - Nettoyage des panics, gestion d’erreurs robuste, logs structurés. - Migrations chat alignées sur le schéma UUID et nouvelles features. Stream server (Rust): - Refonte du moteur de streaming (encoding pipeline + HLS) et des modules core. - Transactions P0 pour les jobs et segments, garanties d’atomicité. - Documentation détaillée de la pipeline (AUDIT_STREAM_*, DESIGN_STREAM_PIPELINE, TRANSACTIONS_P0_IMPLEMENTATION). Documentation & audits: - TRIAGE.md et AUDIT_STABILITY.md à jour avec l’état réel des 3 services. - Cartographie complète des migrations et des transactions (DB_MIGRATIONS_*, DB_TRANSACTION_PLAN, AUDIT_DB_TRANSACTIONS, TRANSACTION_TESTS_PHASE3). - Scripts de reset et de cleanup pour la lab DB et la V1. Ce commit fige l’ensemble du travail de stabilisation P0 (UUID, backend, chat et stream) avant les phases suivantes (Coherence Guardian, WS hardening, etc.).
14 KiB
Fix Sécurité JWT — Rapport complet
Date: 2025-01-27
Faille corrigée: JWT_SECRET avec valeur par défaut hardcodée
Sévérité: 🔴 CRITIQUE
Statut: ✅ CORRIGÉ
1. Fichiers impactés
Fichiers modifiés
-
✅
internal/config/config.go(lignes 115-122)- Avant:
jwtSecret := getEnv("JWT_SECRET", "your-super-secret-jwt-key") - Après:
jwtSecret := getEnvRequired("JWT_SECRET") - Avant:
DatabaseURL: getEnv("DATABASE_URL", "postgresql://veza:password@localhost:5432/veza_db") - Après:
DatabaseURL: getEnvRequired("DATABASE_URL")
- Avant:
-
✅
internal/config/config_test.go(nouveaux tests ajoutés)- Ajout de
TestNewConfig_RequiresJWTSecret()(ligne 287) - Ajout de
TestNewConfig_RequiresDatabaseURL()(ligne 310)
- Ajout de
-
✅
cmd/migrate_tool/main.go(lignes 16-20)- Avant:
Password: getEnv("DB_PASSWORD", "veza") - Après:
Password: getEnvRequired("DB_PASSWORD") - Ajout de la fonction
getEnvRequired()dans ce fichier
- Avant:
-
✅
.env.example(nouveau fichier créé)- Documentation complète des variables d'environnement
- JWT_SECRET et DATABASE_URL marqués comme REQUIS
Fichiers analysés (non modifiés)
internal/config/config.go- FonctionLoad()utilise déjàgetEnvRequired()✅internal/services/jwt_service.go- Gère correctement l'absence de secret ✅internal/config/secrets.go- Liste des secrets correctement définie ✅
2. Autres secrets avec défaut dangereux trouvés
| Variable | Fichier | Action | Statut |
|---|---|---|---|
| JWT_SECRET | internal/config/config.go:116 |
Remplacé par getEnvRequired() |
✅ CORRIGÉ |
| DATABASE_URL | internal/config/config.go:122 |
Remplacé par getEnvRequired() (contient password) |
✅ CORRIGÉ |
| DB_PASSWORD | cmd/migrate_tool/main.go:20 |
Remplacé par getEnvRequired() |
✅ CORRIGÉ |
| DB_PASSWORD (test) | internal/database/pool_test.go:23,86 |
Acceptable (fichier de test uniquement) | ✅ OK |
Variables avec défaut acceptable (gardées)
| Variable | Fichier | Justification |
|---|---|---|
| PORT | config.go:113 |
Valeur par défaut "8080" acceptable pour dev local |
| LOG_LEVEL | config.go:110 |
Valeur par défaut "INFO" acceptable |
| REDIS_URL | config.go:121 |
URL locale par défaut acceptable pour dev |
| CORS_ORIGINS | config.go:101 |
Défaut "*" acceptable pour dev local |
| CHAT_JWT_SECRET | config.go:120 |
Fallback vers JWT_SECRET (maintenant requis) ✅ |
3. Code du fix
3.1 Fonction getEnvRequired() (déjà existante)
// getEnvRequired récupère une variable d'environnement requise (panique si absente)
func getEnvRequired(key string) string {
value := os.Getenv(key)
if value == "" {
panic(fmt.Sprintf("Required environment variable %s is not set", key))
}
return value
}
3.2 Modification dans NewConfig()
AVANT (ligne 116):
jwtSecret := getEnv("JWT_SECRET", "your-super-secret-jwt-key")
APRÈS (ligne 115-116):
// SECURITY: JWT_SECRET est REQUIS - pas de valeur par défaut pour éviter les failles de sécurité
jwtSecret := getEnvRequired("JWT_SECRET")
AVANT (ligne 122):
DatabaseURL: getEnv("DATABASE_URL", "postgresql://veza:password@localhost:5432/veza_db"),
APRÈS (ligne 122-123):
// SECURITY: DATABASE_URL est REQUIS - contient des credentials sensibles
DatabaseURL: getEnvRequired("DATABASE_URL"),
3.3 Correction dans cmd/migrate_tool/main.go
AVANT:
Password: getEnv("DB_PASSWORD", "veza"),
APRÈS:
// SECURITY: DB_PASSWORD is required - no default value to prevent security issues
dbPassword := getEnvRequired("DB_PASSWORD")
// ...
Password: dbPassword,
Avec ajout de la fonction getEnvRequired() dans ce fichier.
4. Tests ajoutés
4.1 Test pour JWT_SECRET manquant
// TestNewConfig_RequiresJWTSecret vérifie que NewConfig() refuse de démarrer sans JWT_SECRET
// Ce test valide la correction de sécurité qui empêche l'utilisation d'une valeur par défaut hardcodée
func TestNewConfig_RequiresJWTSecret(t *testing.T) {
// Sauvegarder les valeurs originales
originalJWTSecret := os.Getenv("JWT_SECRET")
originalDatabaseURL := os.Getenv("DATABASE_URL")
// Nettoyer après le test
defer func() {
if originalJWTSecret != "" {
os.Setenv("JWT_SECRET", originalJWTSecret)
} else {
os.Unsetenv("JWT_SECRET")
}
if originalDatabaseURL != "" {
os.Setenv("DATABASE_URL", originalDatabaseURL)
} else {
os.Unsetenv("DATABASE_URL")
}
}()
// Supprimer JWT_SECRET - devrait causer un panic
os.Unsetenv("JWT_SECRET")
// Définir DATABASE_URL pour éviter un panic sur cette variable (on teste seulement JWT_SECRET)
os.Setenv("DATABASE_URL", "postgresql://test:test@localhost:5432/test_db")
// Devrait paniquer car JWT_SECRET est requis
assert.Panics(t, func() {
_, _ = NewConfig()
}, "NewConfig should panic when JWT_SECRET is missing")
}
4.2 Test pour DATABASE_URL manquant
// TestNewConfig_RequiresDatabaseURL vérifie que NewConfig() refuse de démarrer sans DATABASE_URL
// Ce test valide la correction de sécurité qui empêche l'utilisation d'une valeur par défaut avec credentials
func TestNewConfig_RequiresDatabaseURL(t *testing.T) {
// Sauvegarder les valeurs originales
originalJWTSecret := os.Getenv("JWT_SECRET")
originalDatabaseURL := os.Getenv("DATABASE_URL")
// Nettoyer après le test
defer func() {
if originalJWTSecret != "" {
os.Setenv("JWT_SECRET", originalJWTSecret)
} else {
os.Unsetenv("JWT_SECRET")
}
if originalDatabaseURL != "" {
os.Setenv("DATABASE_URL", originalDatabaseURL)
} else {
os.Unsetenv("DATABASE_URL")
}
}()
// Définir JWT_SECRET (minimum 32 caractères pour passer la validation)
os.Setenv("JWT_SECRET", "test-jwt-secret-key-minimum-32-characters-long")
// Supprimer DATABASE_URL - devrait causer un panic
os.Unsetenv("DATABASE_URL")
// Devrait paniquer car DATABASE_URL est requis
assert.Panics(t, func() {
_, _ = NewConfig()
}, "NewConfig should panic when DATABASE_URL is missing")
}
4.3 Résultat des tests
$ go test ./internal/config -run TestNewConfig_RequiresJWTSecret -v
=== RUN TestNewConfig_RequiresJWTSecret
--- PASS: TestNewConfig_RequiresJWTSecret (0.00s)
PASS
ok veza-backend-api/internal/config 0.015s
✅ Tests passent avec succès
5. Documentation mise à jour
5.1 Fichier .env.example créé
Nouveau fichier créé : veza-backend-api/.env.example
Contenu clé:
- Section "VARIABLES REQUISES" avec JWT_SECRET et DATABASE_URL
- Instructions claires pour générer JWT_SECRET
- Toutes les variables optionnelles documentées avec leurs valeurs par défaut
- Commentaires explicatifs pour chaque variable
Extrait:
# ============================================
# VARIABLES REQUISES (DOIVENT ÊTRE DÉFINIES)
# ============================================
# JWT_SECRET - REQUIS - Secret pour signer et valider les tokens JWT
# DOIT être défini - minimum 32 caractères pour la sécurité
# Générer avec: openssl rand -base64 32
JWT_SECRET=
# DATABASE_URL - REQUIS - URL de connexion à la base de données PostgreSQL
# Format: postgresql://user:password@host:port/database?sslmode=disable
# DOIT être défini - contient des credentials sensibles
DATABASE_URL=
5.2 Documentation existante
- ✅
internal/config/docs.go- JWT_SECRET déjà marqué commeRequired: true - ✅
internal/config/docs_test.go- Tests vérifient que JWT_SECRET est requis - ⚠️ README principal - Ne mentionne pas les variables d'environnement (non critique)
6. Audit secrets supplémentaires
6.1 Recherche exhaustive effectuée
Commandes exécutées:
grep -r "JWT_SECRET" veza-backend-api/
grep -r "jwt.*secret\|secret.*jwt" veza-backend-api/ -i
grep -r "getEnv.*secret\|getEnv.*JWT" veza-backend-api/ -i
grep -r "your-super-secret" veza-backend-api/ -i
grep -r "password\|secret\|api_key" veza-backend-api/internal/config/ -i
6.2 Résultats de l'audit
✅ Secrets correctement gérés
| Secret | Fichier | Statut |
|---|---|---|
| JWT_SECRET | internal/config/config.go |
✅ Corrigé (getEnvRequired) |
| DATABASE_URL | internal/config/config.go |
✅ Corrigé (getEnvRequired) |
| DB_PASSWORD | cmd/migrate_tool/main.go |
✅ Corrigé (getEnvRequired) |
| JWT_SECRET | internal/config/Load() |
✅ Déjà requis (getEnvRequired) |
| DB_PASSWORD | internal/config/Load() |
✅ Déjà requis (getEnvRequired) |
✅ Secrets dans les tests (acceptables)
| Secret | Fichier | Statut |
|---|---|---|
| DB_PASSWORD | internal/database/pool_test.go |
✅ OK (fichier de test uniquement) |
| JWT_SECRET | internal/config/testutils.go |
✅ OK (utilitaire de test) |
✅ Secrets correctement masqués dans les logs
internal/config/secrets.go- FonctionMaskSecret()implémentéeinternal/config/config.go:549- JWT_SECRET masqué dans les logsinternal/config/config.go:550- DATABASE_URL masqué dans les logs
6.3 Aucun secret hardcodé trouvé
✅ Aucune autre valeur par défaut dangereuse trouvée dans le code de production
7. Commandes pour appliquer
7.1 Vérification des modifications
cd veza-backend-api
# Vérifier que le code compile
go build ./internal/config/...
# Exécuter les tests
go test ./internal/config -run TestNewConfig_Requires -v
# Vérifier tous les tests de config
go test ./internal/config/... -v
7.2 Application en production
⚠️ IMPORTANT: Cette correction est BREAKING pour les environnements qui n'ont pas défini JWT_SECRET.
Étapes de déploiement:
-
Avant le déploiement:
# Vérifier que JWT_SECRET est défini dans tous les environnements echo $JWT_SECRET # Ne doit pas être vide echo $DATABASE_URL # Ne doit pas être vide -
Déployer le code:
git add internal/config/config.go internal/config/config_test.go .env.example cmd/migrate_tool/main.go git commit -m "security: Remove hardcoded JWT_SECRET default value - Replace getEnv() with getEnvRequired() for JWT_SECRET in NewConfig() - Replace getEnv() with getEnvRequired() for DATABASE_URL (contains credentials) - Add tests to verify panic when required variables are missing - Create .env.example with clear documentation of required variables - Fix DB_PASSWORD default in migrate_tool BREAKING CHANGE: JWT_SECRET and DATABASE_URL are now required. Application will panic at startup if these variables are not set." git push -
Vérifier le démarrage:
# L'application doit démarrer normalement si les variables sont définies # L'application doit PANIC si JWT_SECRET ou DATABASE_URL sont absents
7.3 Migration des environnements existants
Pour les environnements qui utilisent encore la valeur par défaut:
-
Générer un nouveau JWT_SECRET:
openssl rand -base64 32 -
Définir la variable d'environnement:
export JWT_SECRET="<valeur-générée>" # Ou dans .env: echo "JWT_SECRET=<valeur-générée>" >> .env -
Redémarrer l'application
8. Impact et compatibilité
8.1 Rétrocompatibilité
✅ Rétrocompatible pour les environnements déjà configurés correctement :
- Si
JWT_SECRETest défini → Aucun changement de comportement - Si
DATABASE_URLest défini → Aucun changement de comportement
❌ Breaking change pour les environnements non configurés :
- Si
JWT_SECRETn'est pas défini → Application panic au démarrage - Si
DATABASE_URLn'est pas défini → Application panic au démarrage
8.2 Message d'erreur
En cas de variable manquante, l'application affichera :
panic: Required environment variable JWT_SECRET is not set
ou
panic: Required environment variable DATABASE_URL is not set
Avantage: Message clair et explicite, pas de crash silencieux.
9. Validation finale
✅ Checklist de sécurité
- JWT_SECRET n'a plus de valeur par défaut hardcodée
- DATABASE_URL n'a plus de valeur par défaut avec credentials
- DB_PASSWORD dans migrate_tool corrigé
- Tests ajoutés pour vérifier le comportement
- Documentation créée (.env.example)
- Aucun autre secret avec défaut dangereux trouvé
- Code compile sans erreur
- Tests passent
✅ Tests de validation
# Test 1: Vérifier que NewConfig() panic sans JWT_SECRET
$ go test ./internal/config -run TestNewConfig_RequiresJWTSecret -v
PASS
# Test 2: Vérifier que NewConfig() panic sans DATABASE_URL
$ go test ./internal/config -run TestNewConfig_RequiresDatabaseURL -v
PASS
# Test 3: Compilation
$ go build ./internal/config/...
OK
10. Conclusion
✅ Faille de sécurité corrigée avec succès
- 3 fichiers modifiés pour corriger les valeurs par défaut dangereuses
- 2 nouveaux tests ajoutés pour valider le comportement
- 1 fichier de documentation créé (.env.example)
- Aucun secret hardcodé restant dans le code de production
L'application refuse maintenant de démarrer si JWT_SECRET ou DATABASE_URL ne sont pas définis, empêchant ainsi l'utilisation accidentelle de valeurs par défaut non sécurisées.
Rapport généré le: 2025-01-27
Validé par: Tests automatisés ✅