Samen Steeve
MES SERVICES.
Retour au blog
SécuritéFintechAudit
10 décembre 20257 min de lecture

Audit de sécurité Fintech : retours sur 3 jours d'analyse offensive

Dans le secteur de la fintech en Afrique centrale, le rythme effréné des lancements pousse parfois la sécurité au second plan. Récemment, j'ai réalisé un audit de sécurité offensif de 3 jours sur une plateforme transactionnelle locale de transfert de fonds. Voici les trois vulnérabilités majeures réelles identifiées et comment nous les avons corrigées.

1. Faille BOLA (Broken Object-Level Authorization) sur les reçus

BOLA (anciennement IDOR) trône au sommet du classement OWASP Top 10 API. Sur la plateforme auditée, après un transfert, l'application générait un reçu PDF accessible via l'URL suivante :

GET https://api.fintech-locale.cm/v1/receipts/84209

La faille : Il suffisait de modifier l'ID numérique à la fin de l'URL (ex: passer à 84210) pour télécharger les reçus d'autres utilisateurs. L'API vérifiait que le jeton d'authentification (JWT) était valide, mais elle ne vérifiait pas que l'utilisateur connecté était le propriétaire légitime du reçu demandé.

Le correctif : Remplacement des IDs séquentiels par des UUID v4 non devinables et mise en place d'un contrôle d'autorisation strict au niveau du contrôleur (Laravel Policy) :

// Laravel Policy : Validation stricte de l'ownership
public function view(User $user, Receipt $receipt)
{
    // L'utilisateur doit être l'émetteur ou le destinataire du transfert
    return $user->id === $receipt->sender_id || $user->id === $receipt->recipient_id;
}

2. Force brute sur la vérification OTP (Absence de Rate Limiting)

Pour valider un retrait d'argent, l'API envoyait un code SMS à 4 chiffres (OTP). La requête de validation ressemblait à ceci :

POST https://api.fintech-locale.cm/v1/otp/verify { "transaction_id": 451, "code": "1234" }

La faille : L'endpoint de validation ne possédait aucune limite de requêtes (rate limiting). Un attaquant pouvait faire défiler les 10 000 combinaisons possibles (de 0000 à 9999) en moins de 30 secondes via un simple script d'attaque parallèle, et valider un transfert frauduleux.

Le correctif : Limitation stricte du nombre de tentatives OTP à 3 essais maximum par transaction, couplé à une expiration du code après 2 minutes, et mise en place d'un middleware de limitation de débit IP et utilisateur (Rate Limiter).

3. Injection SQL cachée dans les filtres de rapports dynamiques

Le tableau de bord marchand proposait des rapports personnalisés où l'utilisateur pouvait filtrer ses transactions par colonne. Le code sous-jacent concaténait la colonne de tri directement dans la requête SQL :

// CODE VULNÉRABLE DÉTECTÉ
$sortBy = $request->input('sort_by'); // Ex: "created_at"
$results = DB::select("SELECT * FROM transactions WHERE merchant_id = $id ORDER BY " . $sortBy);

La faille : L'attaquant injectait du code SQL complexe dans le paramètre sort_by (ex: created_at; DROP TABLE users; ou des attaques par inférence de temps pour exfiltrer les hashs de mots de passe de la base de données).

Le correctif : Validation stricte de la variable par rapport à une liste blanche de colonnes autorisées avant l'exécution :

// Validation par liste blanche
$allowedSorts = ['created_at', 'amount', 'status'];
$sortBy = in_array($request->input('sort_by'), $allowedSorts) ? $request->input('sort_by') : 'created_at';

$results = DB::table('transactions')
    ->where('merchant_id', $merchantId)
    ->orderBy($sortBy)
    ->get();

Bilan et recommandations

Ces vulnérabilités ne sont pas des failles cryptographiques complexes, mais des erreurs de logique applicative classiques. Un audit régulier du code et le respect des normes OWASP (vérification systématique des droits d'accès aux objets, limitation stricte de débit sur les flux d'authentification et requêtes paramétrées) sont indispensables pour protéger la confiance des utilisateurs et la sécurité des transactions.

Besoin d'un audit de sécurité offensif ?

Identifions vos failles logiques, vos vulnérabilités BOLA et SSRF avant qu'elles ne soient exploitées.

Démarrer un projet