Trouver la clé API codée en dur : les secrets dans un binaire mobile
Le défi
Cette application Android embarque son secret de paiement de production directement dans le code source. Il y a plus d'une constante suspecte ici, mais une seule est la vraie clé d'API de production que le serveur reconnaît. Lisez le code réseau et tapez cette clé.
Ce que tu vas apprendre
- Reconnaître un identifiant codé en dur dans le code source mobile
- Distinguer un secret de production reconnu par le serveur d'une clé de chiffrement locale faible
- Comprendre pourquoi tout ce qui est embarqué dans un APK peut être extrait par un attaquant
- Lire un intercepteur d'authentification Retrofit/OkHttp pour identifier l'identifiant envoyé
- Expliquer pourquoi les secrets côté client doivent être déplacés vers un backend
Compétences testées
Prérequis
- Lecture basique de Kotlin
- Savoir ce qu'est un jeton Bearer / en-tête Authorization
- Idée générale de la structure d'un APK
Comment ça marche
Les applications mobiles sont distribuées sous forme de fichiers (un APK ou un IPA) qui résident sur l'appareil de l'utilisateur. Tout ce qui est compilé ou intégré dedans, constantes de chaîne, clés, points de terminaison, peut être extrait avec des outils standards comme unzip, strings, apktool, ou un décompilateur. Un secret codé en dur dans le code mobile n'est donc pas un secret ; c'est un identifiant publié.
Dans ApiClient.kt, la constante API_KEY = "sk_live_9f3a2c7d4e1b" est ajoutée à chaque requête sous la forme Authorization: Bearer $API_KEY via un intercepteur OkHttp, puis utilisée par Retrofit pour appeler https://api.acmepay.example. Le préfixe sk_live_ indique qu'il s'agit d'une clé secrète de production (live). Quiconque l'extrait du binaire peut s'adresser à la passerelle de paiement en tant que le marchand : créer des paiements, lire des transactions, ou émettre des remboursements.
Le second fichier est un leurre délibéré. LocalCache.kt utilise une clé AES entièrement composée de zéros en mode ECB, une faiblesse cryptographique réelle, mais elle ne fait que brouiller un cache local et n'est pas l'identifiant authentifié par le serveur. La compétence testée ici est de distinguer un vrai secret reconnu par le serveur d'une faiblesse purement locale. La réponse est la clé API de production, sk_live_9f3a2c7d4e1b.
Erreurs fréquentes
- Soumettre la clé AES 0000000000000000. Elle est faible et constitue un vrai bug, mais elle ne fait qu'obfusquer un cache sur l'appareil, le serveur ne lui fait jamais confiance.
- Nommer la variable (API_KEY) au lieu de sa valeur. La réponse est la chaîne secrète littérale, pas l'identifiant.
- Supposer que la clé est sûre car elle est déclarée private const. Les modificateurs de visibilité n'empêchent pas l'extraction depuis le binaire empaqueté.
- Ignorer le préfixe sk_live_. Ce préfixe indique qu'il s'agit d'un identifiant de production, pas d'une valeur de test ou factice.
Comment s'en protéger
N'intégrez jamais de clés API de production ou d'autres secrets reconnus par le serveur dans un client mobile. Considérez que le binaire de l'application est entièrement lisible par quiconque l'installe.
- Déplacez les appels privilégiés derrière votre propre backend ; l'application authentifie l'utilisateur, et le backend détient le secret de la passerelle.
- Délivrez au client des jetons de courte durée et à portée limitée plutôt qu'une clé maîtresse de longue durée.
- Faites tourner immédiatement toute clé qui a été embarquée dans un client, en considérant qu'elle est déjà compromise.
- Scannez les builds en CI à la recherche de motifs de secrets (par exemple
sk_live_) pour qu'ils n'atteignent jamais une release.