PD-24 — Retour d'expérience¶
📚 Navigation User Story
| Document | | | ---------- | -- | | 📋 [Spécification](PD-24-specification.md) | | | 🛠️ [Plan d'implémentation](PD-24-plan.md) | | | ✅ [Critères d'acceptation](PD-24-acceptability.md) | | | 📝 **Retour d'expérience** | *(ce document)* | [← Retour à crypto-proof](../PD-189-epic.md) · [↑ Index User Story](index.md)1. Résumé exécutif¶
La User Story PD-24 visait à implémenter la Phase 1 du protocole SRP-6a pour une authentification strictement Zero-Knowledge : génération client-side du salt/verifier à l'inscription, et initialisation du challenge SRP à la connexion. L'implémentation fournit un SrpService complet avec groupe 3072 bits (RFC 5054), SHA3-256, validations de sécurité (A mod N ≠ 0), et stockage sessions Redis avec TTL 5 minutes. Cependant, 1 écart MAJEUR est identifié : absence de rate limiting sur /auth/login/challenge malgré l'exigence de protection DoS. Verdict : ACCEPTÉ AVEC RÉSERVES.
2. Points fluides¶
- Paramètres RFC 5054 : groupe 3072 bits (N, g) correctement définis avec référence explicite
- SHA3-256 (Keccak) : utilisation de js-sha3 conforme aux specs ProbatioVault (pas SHA-256)
- BigInt natif : calculs cryptographiques avec
bigintJavaScript, évite les dépendances externes - Fonction modPow : exponentiation modulaire implémentée sans overflow
- Calcul k = SHA3-256(N || PAD(g)) : padding à la taille du groupe respecté
- Validation A mod N ≠ 0 : protection contre l'attaque A=0 implémentée
- Validation verifier ∈ [1, N-1] : rejet des verifiers invalides à l'inscription
- Validation salt 16-32 bytes : contrainte de taille respectée
- Sessions Redis :
SrpSessionStoreServiceavec TTL 5 minutes et suppression après usage - DTOs class-validator :
RegisterDto,LoginChallengeDtoavec @IsHexadecimal, @IsEmail - Tests unitaires : couverture des validations de format et cas limites
- Endpoint GET /auth/srp-params : paramètres publics N, g exposés séparément
- Architecture modulaire :
SrpService+SrpSessionStoreServiceséparés - Documentation inline : commentaires JSDoc décrivant le flux Zero-Knowledge
3. Points difficiles¶
| Difficulté | Contexte |
|---|---|
| Rate limiting absent | Contrainte "Limitation des tentatives" non implémentée sur /auth/login/challenge |
| Réponse challenge divergente | TA-2 attend {salt, B, N, g} mais l'API retourne {salt, B} uniquement |
| Tests RFC 5054 | Aucun test de conformité aux vecteurs officiels RFC 5054 |
| Validation B mod N ≠ 0 | Non testée explicitement dans les tests unitaires |
| Redis obligatoire | Pas de fallback en mémoire, Redis requis même en dev/test |
4. Hypothèses révélées tardivement¶
| Hypothèse initiale | Réalité découverte |
|---|---|
| N/g dans chaque réponse | FAUX — Choix architectural : N/g via endpoint dédié /auth/srp-params, pas dans /login/challenge |
| Tests de conformité RFC 5054 | FAUX — Tests limités aux validations de format, pas de vecteurs officiels |
| Stockage en mémoire pour dev | FAUX — Redis obligatoire dans tous les environnements, tests utilisent ioredis-mock |
| Rate limiting inclus dans scope | FAUX — Mentionné dans spec et plan mais non implémenté |
5. Invariants complexes à implémenter¶
| Invariant | Complexité |
|---|---|
| A mod N ≠ 0 | Sécurité critique, rejet immédiat si violation |
| B mod N ≠ 0 | Génération serveur doit être régénérée si B=0 (très improbable) |
| u ≠ 0 | Rejet si SHA3-256(A |
| Verifier ∈ [1, N-1] | Validation à l'inscription pour éviter les cas dégénérés |
| Padding à GROUP_SIZE | Tous les BigInt doivent être paddés à 384 bytes avant hash |
| Sessions usage unique | Suppression obligatoire après verify pour éviter replay |
| TTL 5 minutes | Protection contre accumulation de sessions abandonnées |
6. Dette technique¶
| Dette | Impact | Priorité |
|---|---|---|
| Rate limiting absent | DoS possible sur /auth/login/challenge | HAUTE |
| Tests RFC 5054 manquants | Conformité non prouvée par tests automatisés | MOYENNE |
| Réponse TA-2 non alignée | Spec/API divergent, documentation à mettre à jour | BASSE |
| Redis obligatoire partout | Complexifie setup dev/CI sans Docker | BASSE |
| Délai aléatoire absent | Protection timing attack non implémentée | MOYENNE |
7. Risques résiduels¶
| Risque | Probabilité | Impact | Mitigation suggérée |
|---|---|---|---|
| DoS /login/challenge | Moyenne | ÉLEVÉ | Implémenter rate limiting (throttler NestJS) |
| Timing attack | Faible | MOYEN | Ajouter délai aléatoire côté serveur |
| Non-conformité RFC 5054 | Faible | ÉLEVÉ | Ajouter tests avec vecteurs officiels |
| Session hijacking | Faible | MOYEN | TTL strict + usage unique déjà en place |
| Redis down | Faible | ÉLEVÉ | Health check + circuit breaker sur Redis |
8. Améliorations processus¶
| Amélioration | Bénéfice attendu |
|---|---|
| Implémenter rate limiting dès la spec | Éviter écart MAJEUR sur contraintes sécurité |
| Inclure vecteurs RFC dans tests | Prouver conformité dès l'implémentation |
| Aligner spec et API avant merge | Éviter divergence TA-2 vs réponse réelle |
| Prévoir fallback mémoire pour tests | Simplifier CI sans Redis réel |
| Tests E2E complets | Valider flux inscription → challenge → verify |
9. Enseignements clés¶
-
Les contraintes de sécurité doivent être testées automatiquement — L'absence de rate limiting n'a pas été détectée car non testée. Les contraintes de la spec (protection DoS, timing attack) doivent avoir des tests correspondants.
-
SHA3-256 ≠ SHA-256 — Le choix de SHA3-256 (Keccak) est correct mais nécessite une bibliothèque dédiée (js-sha3). Les tests de conformité doivent vérifier que ce n'est pas SHA-256 par erreur.
-
BigInt natif suffit pour SRP — Pas besoin de bibliothèques externes (bn.js, bignum) pour les calculs modulaires 3072 bits. L'implémentation de modPow avec BigInt natif est suffisante et plus simple.
-
Redis impose des contraintes d'environnement — Le choix de Redis pour les sessions SRP est robuste (TTL automatique, scalable) mais complexifie les tests et le dev local. L'injection de ioredis-mock via
setRedisClient()est un bon pattern. -
La divergence spec/API doit être détectée tôt — TA-2 attendait
{salt, B, N, g}mais l'implémentation retourne{salt, B}. Ce choix (N/g via endpoint dédié) est valide mais aurait dû être discuté avant l'implémentation pour éviter l'écart documentaire.