Aller au contenu

PD-278 — Revue Sécurité (7c)

Producteur : ChatGPT Relecteur : Claude (synthèse) Date : 2026-03-01 Commit évalué : 410ed7d (feature/PD-278-nfz42013-dip-state)

Contexte

PD-278 ajoute des transitions d'état DIP (Dissemination Information Package) au coffre-fort numérique probatoire NF Z42-013. L'implémentation touche à l'authentification, l'autorisation, le contrôle de concurrence, le rate-limiting, l'audit sécurité, et la protection WORM.

Documents de référence à injecter

  1. Spécification : PD-278-specification.md (§4 Invariants, §6 Cas d'erreur, §7 Critères)
  2. Plan d'implémentation : PD-278-plan.md (§7 Impacts sécurité)
  3. Code contracts : PD-278-code-contracts.yaml

Code source à auditer

Surface d'attaque (par priorité)

  1. Contrôle d'accès :
  2. dissemination.controller.ts : @UseGuards(JwtAuthGuard, AuthorizationGuard, DisseminationRateLimitGuard)
  3. Rôles F1: PA, SA, auditor ; Rôles F2: PA, SA, auditor, retention_service

  4. Protection anti-exfiltration :

  5. dissemination.service.ts : garde retention_due=false sur SEALED→DIP
  6. Mécanisme anti-contournement rétention (INV-278-14)

  7. Rate-limiting :

  8. dissemination-rate-limit.guard.ts : Redis INCR + EXPIRE, fail-closed 503

  9. Audit des refus sécurité :

  10. dissemination-audit-exception.filter.ts : persistance synchrone DOCUMENT_DISSEMINATION_DENIED

  11. Intégrité WORM :

  12. Migration DDL : trigger motif_communication, trigger attestations append-only
  13. REVOKE TRUNCATE sur attestations

  14. Injection SQL :

  15. dissemination.service.ts : requêtes paramétrées $1, $2...

  16. Cryptographie :

  17. hash_evidence : SHA-256 de JSON canonique (RFC 8785)
  18. randomUUID() pour attestation_id, request_id, package_id

Grille d'audit sécurité

1. OWASP Top 10 applicabilité

Catégorie OWASP Risque PD-278 Mitigation implémentée Vérification
A01 Broken Access Control Exfiltration via DIP prolongé retention_due guard + retention_service BYPASSRLS Vérifier que toutes les routes sont protégées
A02 Cryptographic Failures Secrets en clair en base hash_evidence chiffré repos, signature_ref HSM Scanner les requêtes pour secrets clairs
A03 Injection SQL injection via motif_communication Paramètres positionnels $1..$N Vérifier TOUS les queryRunner.query()
A04 Insecure Design Bypass machine à états ALLOWED_TRANSITIONS exhaustive + SELECT FOR UPDATE Fuzz transitions interdites
A07 Auth Failures Enumération utilisateurs Messages d'erreur identiques (DisseminationErrorMessages) Vérifier que 401 et 403 ne leakent pas d'info
A09 Logging Failures Refus non audités Exception filter synchrone 401/403/429/409-RETENTION Vérifier exhaustivité des codes capturés

2. Contrôle d'accès détaillé

  • Les @Roles() sont-ils correctement positionnés sur chaque endpoint ?
  • Le rate-limit guard est-il UNIQUEMENT sur F1 (SEALED→DIP), pas sur F2 ?
  • Le ParseUUIDPipe est-il appliqué sur le param :id de F2 ?
  • L'acteur est-il extrait de user.sub (JWT claim), jamais du body ?

3. Protection anti-contournement rétention (INV-278-14)

  • La garde retention_due=false est-elle vérifiée DANS la transaction (pas en cache) ?
  • Le rôle retention_service peut-il forcer DIP→SEALED même avec RLS ?
  • L'action retention_service est-elle auditée (DOCUMENT_RETURNED) ?

4. Contrôle de concurrence

  • SELECT FOR UPDATE avec ORDER BY id ASC partout ?
  • Le même ordre de verrouillage est-il documenté cross-module (§5.9) ?
  • Timeout de lock configurable pour éviter deadlock infini ?

5. Rate-limiting

  • Fail-closed si Redis down (503, pas bypass) ?
  • Clés Redis avec actorId (pas document_id) ?
  • TTL correctement positionné (60s pour rate, 86400s pour quota) ?
  • Quota journalier reset à 00:00:00Z UTC ?

6. Audit sécurité

  • L'exception filter capture-t-il TOUS les codes de refus sécurité (401, 403, 429, 409-RETENTION-DUE) ?
  • Les codes métier (409-STATE, 409-CONFLICT, 422-GUARD-COPIES) sont-ils EXCLUS (pas de double audit) ?
  • La persistance audit est-elle synchrone (QueryRunner dédié, commit avant response HTTP) ?

7. Intégrité cryptographique

  • hash_evidence calculé côté serveur (jamais client) ?
  • canonicalize() = RFC 8785 (pas JSON.stringify) ?
  • randomUUID() = crypto.randomUUID() (pas Math.random) ?
  • SHA3-256 pour journal, SHA-256 pour attestation : distinction correcte ?

8. Protection WORM

  • Trigger DB bloque UPDATE motif_communication ?
  • Trigger DB bloque UPDATE/DELETE attestations ?
  • REVOKE TRUNCATE sur attestations ?
  • COALESCE(motif_communication, $4) : ne permet-il pas de passer de NULL à une valeur puis de modifier ?

Format de verdict attendu

### Verdict : ✅ / ⚠️ / ❌

### Vulnérabilités identifiées
| ID | Sévérité (CRITIQUE/HAUTE/MOYENNE/BASSE) | Description | Fichier:ligne | Recommandation |
|----|------------------------------------------|-------------|---------------|----------------|

### Points de robustesse
- ...

### Réserves sécurité
- ...