Aller au contenu

PD-277 — Revue Sécurité

Résumé

Critère Statut
Forbidden patterns
Injection SQL
Auth/Authz
Fuite données
Validation

Verdict : ❌ NON_CONFORME

Barrières primaires identifiées

  • OIDC JWT guard + guard de rôle (OidcJwtAuthGuard, LegalPreAccessGuard) sur le contrôleur.
  • Chiffrement at-rest hérité infra (spec INV-277-06, hors logique métier locale).

Lecture defense-in-depth

  • Les écarts nonce (S-01, S-02) touchent la barrière primaire anti-rejeu elle-même -> MAJEUR.
  • Les écarts authz (S-03) ne sont pas compensés par une barrière ownership/RLS visible dans ce flux -> MAJEUR.
  • Les stubs documentés (D-01, D-02) sont classés écarts documentés MINEUR (pas de MAJEUR sans exploit reproductible sur implémentation réelle).

Audit des forbidden patterns

Pattern interdit Recherché Trouvé
Comparaison nonce côté applicatif (doit être DB JSONB @>) usedNonces.includes(nonce)
Acceptation nonce hors format UUID v4 lowercase ASCII regex avec flag i
Retry automatique sur erreur de sérialisation gestion 40001 / retry
Certificats fournis directement par appelant externe input API expose cert IDs
Méthode de modification des certifs après création update cert fields en service/repository

Tentatives de bypass

Attaque Résultat Commentaire
Injection champ protégé { "email": "hack@evil.com" } 400 (attendu) Validation globale whitelist + forbidNonWhitelisted active dans src/main.ts.
Injection SQL legalReKeyId="'; DROP TABLE--" Rejet/échappé (attendu) Paramètres bindés sur where(... :id); pas d'injection exploitable démontrée.
Rejeu nonce par casse (nonce puis NONCE) Bypass possible Regex accepte uppercase (/i), stockage/compare case-sensitive -> second appel peut passer.
Rejeu concurrent (2 requêtes même nonce) Bypass possible Check includes hors verrou DB dédié; race possible avant update JSONB.
Cross-access JWT user A vers ReKey user B Bypass possible Flux consultDocument/getLegalAccessStatus sans ownership check explicite.
Bypass auth sans JWT Refusé (attendu) Guard OIDC global au contrôleur.

Vulnérabilités identifiées

ID Description Gravité Fichier
S-01 Bypass anti-rejeu par variation de casse du nonce. Le format impose lowercase ASCII, mais la regex utilise le flag i. Un attaquant peut réutiliser le même UUID en uppercase/lowercase pour contourner la détection de rejeu basée sur égalité exacte de chaîne. Vecteur: appel 1 avec 550e8400-e29b-41d4-a716-446655440000, appel 2 avec 550E8400-E29B-41D4-A716-446655440000. MAJEUR src/modules/legal-pre/services/legal-rekey-manager.service.ts:386
S-02 Race condition anti-rejeu (TOCTOU). La vérification usedNonces.includes(nonce) est faite côté applicatif sans lock row-level explicite ni contrainte UNIQUE DB sur nonce scope LegalReKey. Deux requêtes concurrentes peuvent valider puis insérer le même nonce. MAJEUR src/modules/legal-pre/services/legal-rekey-manager.service.ts:419
S-03 BOLA/IDOR sur accès ReKey. Les endpoints métier reposent sur un guard de rôle global, mais les opérations consultDocument et getLegalAccessStatus ne vérifient pas que l'appelant est propriétaire/délégué du ReKey ciblé. Un utilisateur légal A connaissant un legalReKeyId B peut accéder aux métadonnées/flux de consultation B. MAJEUR src/modules/legal-pre/controllers/legal-pre.controller.ts:101

Écarts documentés (mock/stub)

ID Description Gravité Fichier
D-01 TspVerifierStub et LegalIdentityResolverStub documentés via TODO/stub; ce ne sont pas des preuves d'implémentation crypto/IAM réelle. Classé dette documentée, pas vulnérabilité critique en soi. MINEUR src/modules/legal-pre/providers/tsp-verifier.stub.ts:7
D-02 consultDocument contient un TODO explicite indiquant que l'appel réseau/crypto réel PreService.reEncrypt() n'est pas branché. Écart de complétude, pas exploit réseau direct au stade actuel. MINEUR src/modules/legal-pre/services/legal-pre-orchestrator.service.ts:349

Recommandations

  • Corriger immédiatement la validation nonce en lowercase strict sans flag i et rejeter toute variante non canonique (ERR-NONCE-FORMAT).
  • Déplacer la détection d'unicité nonce au niveau DB atomique (ex: UPDATE ... WHERE NOT used_nonces @> ... RETURNING) ou table dédiée legal_rekey_nonce avec contrainte UNIQUE (legal_rekey_id, nonce) + SERIALIZABLE.
  • Retirer la logique includes() applicative pour se conformer au forbidden pattern PD-277 (unicité gérée côté DB uniquement).
  • Ajouter un contrôle d'autorisation objet (ownership/delegation) sur consultDocument, closeAccess, getLegalAccessStatus et renvoyer une erreur opaque en cas d'accès cross-tenant.
  • Ajouter des tests adversariaux e2e reproduisant: (1) rejeu par casse, (2) rejeu concurrent, (3) cross-access user A -> ReKey B.