Skip to main content

Sécurité : plusieurs accès non protégés corrigés, et un filtre par zone qui ne fonctionnait pas réellement

Équipe Digitalisation UTB
Frontend, Backend & Backoffice

Une passe d'audit ciblée a mis au jour plusieurs endpoints backend accessibles sans authentification ou sans vérification du périmètre agence/zone de l'utilisateur — tous corrigés. Le cas le plus instructif : un correctif déjà appliqué au filtrage par zone s'est révélé totalement inopérant, découvert seulement en testant avec un vrai compte connecté plutôt qu'en relisant le code.

Ce qui était exposé

  • PUT /api/loan-parameters/:id — aucune authentification requise : n'importe qui pouvait modifier un taux d'intérêt ou un plafond de prêt.
  • GET /api/alertes et GET /api/alertes/:id — aucune authentification requise : n'importe qui pouvait lister l'intégralité des signalements confidentiels (fraude, questions sensibles).
  • POST/PUT/DELETE /api/users — authentification requise mais sans restriction de rôle : n'importe quel compte backoffice pouvait créer, modifier ou désactiver un autre utilisateur, y compris se promouvoir SUPER_ADMIN.
  • GET /api/account-requests et GET /api/account-requests/:id — aucune authentification requise : identité, téléphone, email, adresse et données FATCA/TIN de toutes les demandes d'ouverture de compte étaient publiquement listables.
  • PATCH /api/loan-requests/:id (updateLoanStatus) — authentifié, mais sans aucune vérification d'appartenance à une agence : n'importe quel utilisateur backoffice pouvait changer le statut de n'importe quelle demande de prêt, y compris celles d'une autre agence. Le point le plus sensible du contrôleur puisqu'il exécute la décision, pas une simple consultation.

Toutes ces routes exigent désormais une authentification (verifyToken), et les deux dernières sont en plus scopées par agence/zone.

Le filtre par zone (CHEF_ZONE) : corrigé, cassé à nouveau, puis vraiment corrigé

Un module partagé (backend/src/utils/agency-scope.js, agencyScopeWhere/canAccessAgency) avait été écrit pour que le rôle CHEF_ZONE ne voie que les agences de sa propre zone, au lieu de tout voir comme un SUPER_ADMIN. Ce correctif est resté inopérant pour deux raisons cumulées, découvertes uniquement en connectant un vrai compte CHEF_ZONE (login + 2FA + appels API réels, pas une relecture de code) :

  1. CHEF_ZONE avait la permission view_all_agencies dans seed/seed_roles.js — cette permission court-circuite en premier toute la logique de filtrage, rendant la restriction par zone écrite juste en dessous inatteignable. Retirée des permissions du rôle.
  2. Une fois ce point corrigé, plus aucun dossier n'était visible pour CHEF_ZONE — régression inverse. Cause : id_zone n'était jamais inclus dans le JWT émis à la connexion (verify2fa), seul id_agence l'était. Un CHEF_ZONE n'a pas de id_agence (null), donc le filtre de repli ne matchait jamais rien. Corrigé en ajoutant id_zone au payload du token et à getMe.

Vérifié par un test de bout en bout : un CHEF_ZONE de la zone Maritime voit les dossiers de son agence, et reçoit un 403 sur une agence d'une autre zone.

Pourquoi ce post existe

Le premier correctif du filtre par zone avait été documenté comme terminé sans avoir été testé en conditions réelles — il avait l'air correct en lecture de code tout en étant complètement sans effet. La leçon retenue et appliquée depuis : vérifier par un vrai appel authentifié avant de marquer un correctif de sécurité comme résolu, pas seulement relire le code.

Pages de doc à jour

Bugs & failles actives connues, Agences, zones & GAB, Authentification & 2FA, Fiche détail d'une demande de compte.