Skip to content
acceptedAudienceDevSécuritéAudit banqueOwner@security-teamDernière revue2026-05-21

ADR-0013 — QR code sécurisé AES-256-GCM avec device key par appareil

Status : Accepted Date : 2026-03 (rétroactif) Deciders : équipe sécurité Nex Tickets :

Contexte

L’app Nex permet le paiement par QR code (le payeur scanne, ou se fait scanner). Risques :

  • Replay : un QR capté en photo ou screenshot rejoué pour vol.
  • Forgery : un attaquant fabrique un QR pour piéger un payeur.
  • MITM : interception d’un QR dans le canal d’affichage.

Le QR doit être à la fois court (lisible par caméra fiable) et vérifiable par le serveur.

Décision

  1. Chiffrement : AES-256-GCM (authenticated encryption — confidentialité + intégrité + auth).
  2. Une clé par appareil : à l’enrôlement, le serveur génère une device_key (AES-256 hex 64 chars) stockée dans qr_device_keys (entity TypeORM côté auth).
  3. Payload chiffré = userId, deviceId, nonce, expiresAt.
  4. Nonce garantit l’unicité (anti-replay au niveau cryptographique, vérifié serveur via cache court).
  5. Tag d’authentification GCM rejeté à la résolution = QR invalide.
  6. Rotation : la device key peut être révoquée et remplacée à tout moment côté serveur.
  7. Stockage côté app : la clé n’est pas stockée côté mobile — seul le QR final est stocké (limite la portée d’une compromission appareil).

Conséquences

Positives

  • Confidentialité + intégrité + authenticité dans une seule primitive éprouvée.
  • Anti-replay natif via nonce + TTL court.
  • Révocation par appareil sans toucher les autres.
  • Surface mobile minimale (pas de gestion de clé côté app).

Négatives / risques

  • Si la device key fuite côté serveur, tous les QR signés par elle sont compromis. Mitigation : qr_device_keys n’est accessible que côté auth, et la clé est stockée chiffrée at-rest (à confirmer cf. ADR-0008 et /security/encryption-standards).
  • Le serveur doit valider rapidement : ajouter du cache sans casser l’anti-replay.
  • Pas de SSL pinning mobile aujourd’hui (/security/known-gaps) — un attaquant qui peut intercepter le trafic peut voler un QR avant qu’il ne soit consommé.

Alternatives écartées

  • HMAC seul — pas de confidentialité, le payload est lisible (révèle userId, etc.).
  • RSA signature — payload trop volumineux pour QR, signature seule sans chiffrement.
  • Une clé globale partagée — compromission catastrophique.
  • JWT signé symétrique — proche de la décision retenue mais sans confidentialité native.

Suivi

  • Voir /security/qr-code pour le détail d’implémentation.
  • Code : services/auth/src/auth/qr-validation.service.ts, entity qr-device-key.entity.ts.
  • Rate limiting sur POST /qr/resolve à appliquer/renforcer (cf. known-gaps).
  • Migration future vers URL HTTPS au lieu de payload AES embarqué — non priorisée.

Nex — Plateforme fintech CEMAC