N'importe qui peut télécharger votre APK et lire les chaînes qu'il contient. La faiblesse OWASP MASWE-0004 le formule sans détour : une donnée sensible intégrée au paquet de l'application part avec elle, et toute personne qui télécharge le paquet peut la récupérer. La documentation Android sur les secrets cryptographiques codés en dur ajoute qu'un attaquant équipé d'outils de rétro-ingénierie récupère un tel secret très facilement.
Cet article décrit où les secrets se retrouvent dans un build, quelles clés sont réellement secrètes, et ce qu'il faut changer avant la prochaine version.
Où les secrets se retrouvent dans un build
MASWE-0004 recense quatre façons dont une donnée sensible entre dans un paquet :
- Le code source : une clé écrite en constante dans une classe compilée dans l'application.
- Les assets et ressources :
strings.xml, des fichiers JSON ou properties sousassets/, le manifeste. - Les bibliothèques : la configuration ou le code d'une dépendance interne, tierce ou transitive.
- Les résidus de build : URL de préproduction ou d'intégration, adresses e-mail de développeurs, fichiers source ou fichiers map embarqués par erreur.
Les deux derniers passent facilement inaperçus, parce que personne dans l'équipe ne les a écrits volontairement. Un secret présent dans la configuration d'une dépendance se retrouve quand même dans votre APK.
L'obfuscation ne cache pas une chaîne
La documentation Android sur R8 décrit l'obfuscation comme le raccourcissement des noms de classes, de champs et de méthodes, pour réduire la taille du fichier DEX. Cette description ne touche pas aux valeurs des chaînes : une clé stockée sous forme de littéral reste une chaîne lisible dans le DEX après le passage de R8.
MASWE-0004 range la cryptographie en boîte blanche, l'obfuscation du code et le RASP parmi les solutions de dernier recours. Ces techniques dissuadent sans empêcher l'extraction, et ne doivent pas remplacer le déplacement du secret hors de l'application.
Toutes les clés présentes dans un APK ne sont pas des secrets
Certaines clés sont conçues pour être embarquées dans le client. Les traiter comme des fuites fait perdre du temps. Le vrai risque est l'inverse : prendre un vrai secret pour un simple identifiant.
Firebase. La documentation Firebase sur les clés API indique que les clés des services Firebase servent seulement à identifier votre projet et votre application auprès de ces services, et que les clés limitées aux services Firebase n'ont pas à être traitées comme des secrets. L'accès est contrôlé par IAM, les Firebase Security Rules et App Check. La même page cite une exception : les clés de l'API Gemini Developer ne doivent jamais figurer dans le code ni dans les fichiers de configuration.
Google Maps Platform. Une clé Maps utilisée par une application Android doit porter une restriction d'application (le nom de package et l'empreinte SHA-1 du certificat de signature) et une restriction aux seules API appelées. Voir les bonnes pratiques de sécurité des API.
Tout ce qui donne un accès à lui seul est un secret : un secret d'API côté serveur, la clé secrète d'un prestataire de paiement, des identifiants cloud, un jeton vers votre propre backend qui n'expire jamais, une clé symétrique qui chiffre des données utilisateur. Si la personne qui détient la valeur peut appeler le service à votre place, cette valeur ne doit pas partir dans l'application.
Que faire d'un vrai secret
Le garder sur un serveur. Les bonnes pratiques de gestion des clés API de Google Cloud précisent que des clés codées en dur dans le code source ou stockées dans un dépôt sont exposées à l'interception et au vol. L'application appelle votre serveur, qui ajoute l'identifiant. La documentation Maps fixe deux règles pour ce proxy : exiger une authentification des clients, et ne pas laisser les clients relayer n'importe quel appel d'API à travers lui.
Utiliser des identifiants à durée de vie courte. MASWE-0004 recommande des services d'API avec une vraie authentification et une gestion de session, des jetons qui expirent rapidement (le texte cite une heure en exemple), et renvoie vers OAuth 2.0 et AppAuth.
Générer les clés de l'appareil sur l'appareil. Pour les clés dont l'application a besoin localement, Android recommande l'Android Keystore pour des identifiants utilisables par la seule application, ou l'API KeyChain pour des identifiants partagés par le système. Une clé générée dans le Keystore n'apparaît jamais dans l'APK.
Restreindre ce qui doit rester. Quand une clé doit rester dans le client, MASWE-0004 demande de lui donner le minimum de permissions et de restrictions, pour qu'une copie vaille le moins possible.
Une clé déjà publiée
Retirer une clé de la version suivante ne la retire pas des builds déjà installés. La documentation Maps le dit clairement : dans les applications Android et iOS, les clés ne sont remplacées que lorsque les utilisateurs mettent à jour l'application.
Pour un secret présent dans un build publié :
- Le considérer comme exposé, puis le renouveler ou le révoquer chez le fournisseur.
- Si d'anciennes versions en dépendent encore, le restreindre d'abord, puis vérifier son usage avant de le supprimer, comme le recommande la documentation Maps.
- Faire passer l'appel par votre serveur, pour que la clé suivante n'atteigne jamais le client.
Google Cloud recommande aussi de créer régulièrement de nouvelles clés, de supprimer les anciennes et de ne garder que celles réellement utilisées.
Vérifier son propre build avant publication
Lancez ces commandes sur l'APK de release et non sur un build de debug : les types de build peuvent porter des valeurs différentes.
Lister tous les fichiers du paquet, puis lire les fichiers de configuration sous assets/ :
unzip -l app-release.apk
unzip -p app-release.apk assets/config.json
Afficher le pool de chaînes des ressources avec aapt2 (fourni avec les build tools du SDK Android) et le filtrer :
aapt2 dump strings app-release.apk | grep -i -E 'key|secret|token|password'
Chercher dans les chaînes compilées dans les fichiers DEX :
unzip -p app-release.apk 'classes*.dex' | strings -n 8 | grep -i -E 'api[_-]?key|secret|bearer'
Ces recherches ne trouvent que ce que vous avez pensé à chercher. Cherchez aussi les préfixes exacts des fournisseurs que vous utilisez et vos noms d'hôte de préproduction.
Côté code source, MASWE-0004 suggère d'auditer le code et ses dépendances avec un détecteur de secrets comme gitleaks. Pour les clés cryptographiques, le test MASTG MASTG-TEST-0212 recherche les clés construites à partir d'un tableau d'octets codé en dur, en général passé à SecretKeySpec.
Référentiels
- Contrôle OWASP MASVS : MASVS-STORAGE-1, « The app securely stores sensitive data » (l'application stocke les données sensibles de façon sûre).
- Faiblesse : MASWE-0004, inscrite au profil MAS-L1, le socle applicable à toute application.
- CWE : CWE-798 (identifiants codés en dur), CWE-321 (clé cryptographique codée en dur).
Sources officielles
- OWASP MASWE-0004 : Sensitive Data Hardcoded in the App Packagemas.owasp.org
- Android Developers : Hardcoded Cryptographic Secretsdeveloper.android.com
- Android Developers : réduction et obfuscation du code avec R8developer.android.com
- Firebase : Learn about and manage API keys for Firebasefirebase.google.com
- Google Maps Platform : API security best practicesdevelopers.google.com
- Google Cloud : Best practices for managing API keysdocs.cloud.google.com
- Android Developers : Android Keystore systemdeveloper.android.com
- Android Developers : AAPT2developer.android.com
- OWASP MASTG-TEST-0212 : Use of Hardcoded Cryptographic Keys in Codemas.owasp.org