La plupart des développeurs d'applications intégreront des bibliothèques tierces dans leurs applications. S'il s'agit d'accéder à un service, tel que Dropbox ou YouTube, ou de consigner des plantages. Le nombre de bibliothèques et de services tiers est stupéfiant. La plupart de ces bibliothèques et services sont intégrés en s’authentifiant d’une manière ou d’une autre avec le service. La plupart du temps, cela se fait via une clé API. Pour des raisons de sécurité, les services génèrent généralement une clé publique et privée, souvent appelée clé secrète. Malheureusement, pour pouvoir se connecter aux services, cette clé privée doit être utilisée pour s’authentifier et donc faire probablement partie de l’application. Inutile de dire que cela pose un problème de sécurité immense. Les clés d'API publiques et privées peuvent être extraites des fichiers APK en quelques minutes et peuvent être facilement automatisées.
En supposant que j'ai quelque chose de similaire à ceci, comment puis-je protéger la clé secrète:
public class DropboxService {
private final static String APP_KEY = "jk433g34hg3";
private final static String APP_SECRET = "987dwdqwdqw90";
private final static AccessType ACCESS_TYPE = AccessType.DROPBOX;
// SOME MORE CODE HERE
}
Quel est à votre avis le moyen le plus sûr et le plus sûr de stocker la clé privée? Obfuscation, cryptage, qu'en pensez-vous?
Dans l’état actuel, votre application compilée contient les chaînes de clé, mais également les noms de constante APP_KEY et APP_SECRET. Extraire les clés d'un tel code auto-documenté est trivial, par exemple avec l'outil standard Android dx.
Vous pouvez appliquer ProGuard. Les chaînes de clé resteront intactes, mais les noms des constantes seront supprimés. Il renommera également les classes et les méthodes avec des noms courts et sans signification, chaque fois que cela sera possible. Extraire les clés prend alors un peu plus de temps, pour déterminer quelle chaîne de caractères sert à quoi.
Notez que la configuration de ProGuard ne devrait pas être aussi difficile que vous le craignez. Pour commencer, vous devez uniquement activer ProGuard, comme indiqué dans le fichier project.properties. En cas de problème avec les bibliothèques tierces, il peut être nécessaire de supprimer certains avertissements et/ou d'empêcher qu'ils ne soient masqués, dans le fichier proguard-project.txt. Par exemple:
-dontwarn com.dropbox.**
-keep class com.dropbox.** { *; }
Ceci est une approche de force brute; vous pouvez affiner cette configuration une fois que l’application traitée fonctionne.
Vous pouvez obscurcir les chaînes manuellement dans votre code, par exemple avec un encodage Base64 ou de préférence avec quelque chose de plus compliqué; peut-être même du code natif. Un pirate informatique devra alors procéder à un reverse engineering statique de votre codage ou intercepter dynamiquement le décodage au bon endroit.
Vous pouvez appliquer un obscurcissement commercial, comme le frère spécialisé de ProGuard DexGuard . Il peut en outre chiffrer/masquer les chaînes et les classes pour vous. Extraire les clés prend alors encore plus de temps et d’expertise.
Vous pourrez peut-être exécuter des parties de votre application sur votre propre serveur. Si vous pouvez garder les clés, elles sont en sécurité.
À la fin, c’est un compromis économique que vous devez faire: quelle importance accordez-vous aux clés, combien de temps ou de logiciels pouvez-vous vous permettre, combien sophistiqués sont les pirates informatiques qui s’intéressent aux clés, combien de temps vont-ils vouloir dépenser, combien de temps vaut un délai avant le piratage des clés, à quelle échelle tout pirate informatique performant distribuera-t-il les clés, etc. De petites informations, telles que des clés, sont plus difficiles à protéger que des applications entières. Intrinsèquement, rien du côté du client n'est indestructible, mais vous pouvez certainement relever la barre.
(Je suis le développeur de ProGuard et de DexGuard)
Quelques idées, à mon avis, seule la première donne une garantie:
Conservez vos secrets sur un serveur sur Internet et, au besoin, récupérez-les et utilisez-les. Si l'utilisateur est sur le point d'utiliser Dropbox, rien ne vous empêche de faire une demande à votre site et d'obtenir votre clé secrète.
Mettez vos secrets dans le code jni, ajoutez du code variable pour rendre vos bibliothèques plus grandes et plus difficiles à décompiler. Vous pouvez également scinder la chaîne de clé en quelques parties et les conserver à divers endroits.
utiliser obfuscator, également mettre dans le code hashed secret et plus tard le déchaîner quand nécessaire.
Placez votre clé secrète en tant que derniers pixels d'une image parmi vos ressources. Puis, au besoin, lisez-le dans votre code. Brouiller votre code devrait aider à cacher le code qui le lira.
Si vous voulez avoir un aperçu de la facilité avec laquelle vous lisez le code apk, saisissez APKAnalyser:
http://developer.sonymobile.com/knowledge-base/tool-guides/analyse-your-apks-with-apkanalyser/
Une autre approche consiste à ne pas avoir le secret sur l'appareil en premier lieu! Voir Techniques de sécurité de l'API mobile (en particulier la partie 3).
En vous servant de la tradition éprouvée de l'indirection, partagez le secret entre votre point de terminaison d'API et un service d'authentification d'application.
Lorsque votre client souhaite effectuer un appel d'API , il demande au service d'authentification de l'application de l'authentifier (à l'aide de techniques d'attestation distantes puissantes), et il reçoit un délai limité (généralement JWT) jeton signé par le secret.
Le jeton est envoyé avec chaque appel d'API où le noeud final peut vérifier sa signature avant d'agir sur la demande.
Le secret réel n'est jamais présent sur l'appareil; En fait, l'application n'a jamais aucune idée de sa validité ou non, elle interrompt les demandes d'authentification et transmet le jeton résultant. Comme avantage Nice de l’indirection, si vous souhaitez modifier le secret, vous pouvez le faire sans exiger que les utilisateurs mettent à jour leurs applications installées.
Donc, si vous voulez protéger votre secret, ne pas l'avoir dans votre application est un très bon moyen.
Nous pouvons utiliser Gradle pour sécuriser la clé API ou la clé secrète.
1. gradle.properties (propriétés du projet): Créez une variable avec une clé.
GoolgeAPIKey = "Your API/Secret Key"
2. build.gradle (Module: app): Définissez la variable dans build.gradle pour y accéder en activité ou en fragment. Ajoutez le code ci-dessous à buildTypes {}.
buildTypes.each {
it.buildConfigField 'String', 'GoogleSecAPIKEY', GoolgeAPIKey
}
3. Accédez-le dans Activity/Fragment by BuildConfig de l'application:
BuildConfig.GoogleSecAPIKEY
La solution ci-dessus est utile dans le projet open source pour s’engager sur Git. (Merci à David Rawson et à riyaz-ALi pour votre commentaire).
Selon les commentaires de Matthew et Pablo Cegarra, la méthode ci-dessus n’est pas sécurisée et Decompiler permettra à quelqu'un de voir BuildConfig avec nos clés secrètes.
Solution:
Nous pouvons utiliser NDK pour sécuriser les clés de l'API. Nous pouvons stocker des clés dans la classe native C/C++ et y accéder dans nos classes Java.
Suivez s'il vous plaît this blog pour sécuriser les clés d'API avec NDK.
n suivi sur la façon de stocker les jetons en toute sécurité dans Android
Une solution possible consiste à coder les données dans votre application et à utiliser le décodage au moment de l'exécution (lorsque vous souhaitez utiliser ces données). Je recommande également d'utiliser progaurd pour rendre plus difficile la lecture et la compréhension du code source décompilé de votre application. Par exemple, j'ai mis une clé codée dans l'application, puis une méthode de décodage dans mon application pour décoder mes clés secrètes au moment de l'exécution:
// "the real string is: "mypassword" ";
//encoded 2 times with an algorithm or you can encode with other algorithms too
public String getClientSecret() {
return Utils.decode(Utils
.decode("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
Le code source décompilé d’une application proguardée est le suivant:
public String c()
{
return com.myrpoject.mypackage.g.h.a(com.myrpoject.mypackage.g.h.a("Ylhsd1lYTnpkMjl5WkE9PQ=="));
}
Au moins c'est assez compliqué pour moi. C'est ce que je fais quand je n'ai pas d'autre choix que de stocker une valeur dans mon application. Bien sûr, nous savons tous que ce n'est pas la meilleure façon, mais cela fonctionne pour moi.
/**
* @param input
* @return decoded string
*/
public static String decode(String input) {
// Receiving side
String text = "";
try {
byte[] data = Decoder.decode(input);
text = new String(data, "UTF-8");
return text;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "Error";
}
Version décompilée:
public static String a(String paramString)
{
try
{
str = new String(a.a(paramString), "UTF-8");
return str;
}
catch (UnsupportedEncodingException localUnsupportedEncodingException)
{
while (true)
{
localUnsupportedEncodingException.printStackTrace();
String str = "Error";
}
}
}
et vous pouvez trouver autant de classes de cryptage avec une petite recherche dans google.
La clé App-Secret doit rester confidentielle. Toutefois, lorsque vous relâchez l'application, certains peuvent l'inverser.
pour ces gars-là, il ne se cachera pas, verrouillez le code ProGuard
. C'est un refactor et certains obscurcisseurs payants insèrent quelques opérateurs au niveau des bits pour récupérer la chaîne jk433g34hg3
. Vous pouvez prolonger le piratage de 5 à 15 minutes si vous travaillez 3 jours :)
Le meilleur moyen est de le garder tel quel, à mon humble avis.
Même si vous stockez sur le serveur (votre PC), la clé peut être piratée et imprimée. Peut-être que cela prend le plus longtemps? Quoi qu’il en soit, c’est quelques minutes ou quelques heures dans le meilleur des cas.
Un utilisateur normal ne décompilera pas votre code.
En ajoutant à la solution @Manohar Reddy, firebase Database ou firebase RemoteConfig (avec la valeur par défaut Null) peuvent être utilisés:
Qu'est-ce qui est différent dans cette solution?
Cet exemple présente différents aspects. Je mentionnerai quelques points qui, à mon avis, n’ont pas été explicitement couverts ailleurs.
Protéger le secret en transit
La première chose à noter est que pour accéder à l'API dropbox à l'aide de leur mécanisme authentification d'application , vous devez transmettre votre clé et votre secret. La connexion est HTTPS, ce qui signifie que vous ne pouvez pas intercepter le trafic sans connaître le certificat TLS. Cela empêche une personne d'intercepter et de lire les paquets lors de son trajet de l'appareil mobile au serveur. Pour les utilisateurs normaux, c'est un très bon moyen de garantir la confidentialité de leur trafic.
Ce à quoi il n'est pas bon, c'est d'empêcher une personne malveillante de télécharger l'application et d'inspecter le trafic. Il est vraiment facile d'utiliser un proxy intermédiaire pour tout le trafic entrant et sortant d'un appareil mobile. En raison de la nature de l'API Dropbox, l'extraction de la clé et du secret de l'application ne nécessite aucun désassemblage ni aucune ingénierie inverse du code.
Vous pouvez faire épingler ce qui vérifie que le certificat TLS que vous recevez du serveur est celui que vous attendez. Cela ajoute un contrôle au client et rend plus difficile l'interception du trafic. Cela rendrait plus difficile l'inspection du trafic en vol, mais la vérification d'épinglage ayant lieu dans le client, il serait donc toujours possible de désactiver le test d'épinglage. Cela rend cependant plus difficile.
Protéger le secret au repos
Dans un premier temps, utiliser quelque chose comme proguard contribuera à rendre moins évident la localisation des secrets. Vous pouvez également utiliser le NDK pour stocker la clé et le secret et envoyer directement des demandes, ce qui réduirait considérablement le nombre de personnes disposant des compétences appropriées pour extraire les informations. Une obfuscation supplémentaire peut être obtenue en ne stockant pas les valeurs directement dans la mémoire pour une durée indéterminée. Vous pouvez les chiffrer et les déchiffrer juste avant leur utilisation, comme suggéré par une autre réponse.
Options plus avancées
Si vous êtes maintenant paranoïaque à l'idée de révéler le secret n'importe où dans votre application et que vous avez le temps et l'argent nécessaires pour investir dans des solutions plus complètes, vous pouvez envisager de stocker les informations d'identification sur vos serveurs (en supposant que vous en ayez). Cela augmenterait la latence de tous les appels vers l'API, car elle devra communiquer via votre serveur, et pourrait augmenter les coûts d'exploitation de votre service en raison de l'augmentation du débit de données.
Vous devez ensuite décider de la meilleure façon de communiquer avec vos serveurs pour vous assurer qu'ils sont protégés. Ceci est important pour éviter que tous les mêmes problèmes ne se reproduisent avec votre API interne. La meilleure règle que je puisse vous donner est de ne transmettre aucun secret directement à cause de la menace "homme du milieu". Au lieu de cela, vous pouvez signer le trafic en utilisant votre secret et vérifier l’intégrité de toutes les demandes qui arrivent sur votre serveur. Une méthode standard consiste à calculer un HMAC du message associé à un secret. Je travaille dans une entreprise qui propose un produit de sécurité qui intervient également dans ce domaine, ce qui explique pourquoi ce genre de choses m'intéresse. En fait, voici un article blog de l'un de mes collègues qui traite de la plupart de ces questions.
Combien dois-je faire?
Avec de tels conseils de sécurité, vous devez décider du coût/bénéfice de votre décision. Si vous êtes une banque protégeant des millions de clients, votre budget est totalement différent de celui d'une personne prenant en charge une application dans leur temps libre. Il est pratiquement impossible d'empêcher une personne de porter atteinte à votre sécurité, mais dans la pratique, peu de gens ont besoin de toutes les fonctionnalités et les précautions de base vous permettront de faire beaucoup de chemin avec ce que vous faites.
La solution la plus sécurisée consiste à conserver vos clés sur un serveur et à acheminer toutes les demandes nécessitant cette clé sur votre serveur. De cette façon, la clé ne quitte jamais votre serveur. Par conséquent, tant que votre serveur est sécurisé, votre clé l'est aussi. Bien sûr, cette solution a un coût de performance.
Âge vieux poste, mais toujours assez bon. Je pense que le cacher dans une bibliothèque .so serait bien, en utilisant NDK et C++ bien sûr. Les fichiers .so peuvent être visualisés dans un éditeur hexadécimal, mais bonne chance pour décompiler cela: P
Quoi que vous fassiez pour sécuriser vos clés secrètes ne sera pas une vraie solution. Si le développeur peut décompiler l’application, il n’ya aucun moyen de sécuriser la clé. Cacher la clé n’est qu’une sécurité obscure, de même que l’obscurcissement du code. Le problème avec la sécurisation d'une clé secrète est que, pour la sécuriser, vous devez utiliser une autre clé et que cette clé doit également être sécurisée. Pensez à une clé cachée dans une boîte qui est verrouillée avec une clé. Vous placez une boîte dans une pièce et verrouillez la pièce. Il vous reste une autre clé à sécuriser. Et cette clé va toujours être codée en dur dans votre application.
Donc, à moins que l'utilisateur n'entre un PIN ou une phrase, il n'y a aucun moyen de cacher la clé. Mais pour ce faire, vous auriez besoin d'un système de gestion des codes confidentiels hors bande, c'est-à-dire via un canal différent. Certainement pas pratique pour sécuriser des clés pour des services tels que les API Google.
Gardez le secret dans firebase database
et obtenez-en quand l'application démarre, c'est bien mieux que d'appeler un service Web.
Le seul véritable moyen de garder ces données privées est de les conserver sur votre serveur et de laisser l'application envoyer quoi que ce soit au serveur, et le serveur interagit avec Dropbox. De cette façon, vous ne distribuez JAMAIS votre clé privée dans aucun format.
Sur la base d'une expérience amère et après avoir consulté un expert IDA-Pro, la meilleure solution consiste à déplacer une partie essentielle du code dans une DLL/SharedObject, puis de la récupérer depuis un serveur et de la charger au moment de l'exécution.
Les données sensibles doivent être encodées car il est très facile de faire quelque chose comme ceci:
$ strings libsecretlib.so | grep My
My_S3cr3t_P@$$W0rD