Comment puis-je protéger le nom d'utilisateur et le mot de passe MySQL de la décompilation?

87

Les .classfichiers Java peuvent être décompilés assez facilement. Comment puis-je protéger ma base de données si je dois utiliser les données de connexion dans le code?

Jakob Cosoroaba
la source
J'espère que cela ne vous dérange pas, j'ai repensé votre question. J'ai supprimé "username" et "password" et ajouté "reverse-engineering" et "decompiling". Je pense qu'ils sont plus descriptifs que les originaux. Excellente question sur les fondamentaux, au fait!
William Brendel
6
Notez que le fait que vous utilisez Java n'est pas vraiment pertinent ici. Avoir des mots de passe codés en dur dans n'importe quel langage est problématique à peu près de la même manière ("strings thebinary" montre aussi bien les constantes de chaîne dans les programmes C).
Joachim Sauer le
@saua: C'est vrai, mais peut-être que quelqu'un publiera un exemple de code sur la façon de découpler les noms d'utilisateur et les mots de passe en Java. Je pourrais même le faire moi-même si j'en ai le temps.
William Brendel
1
J'ai remarqué que beaucoup de réponses pensent que vous essayez de cacher le nom d'utilisateur / les mots de passe à un utilisateur non autorisé pendant que l'utilisateur exécutant l'application est ok. Je crois que vous voulez cacher le mot de passe à TOUT LE MONDE . Veuillez clarifier cela dans la question.
pek
1
Par exemple, disons que les informations d'identification sont utilisées pour se connecter à un serveur auquel personne d'autre que l'application ne peut se connecter.
pek le

Réponses:

123

Ne codez jamais de mots de passe en dur dans votre code. Cela a été récemment évoqué dans le Top 25 des erreurs de programmation les plus dangereuses :

Le codage en dur d'un compte secret et d'un mot de passe dans votre logiciel est extrêmement pratique - pour les ingénieurs inverseurs qualifiés. Si le mot de passe est le même pour tous vos logiciels, chaque client devient vulnérable lorsque ce mot de passe devient inévitablement connu. Et parce que c'est codé en dur, c'est une énorme douleur à réparer.

Vous devez stocker les informations de configuration, y compris les mots de passe, dans un fichier séparé que l'application lit au démarrage. C'est le seul vrai moyen d'empêcher le mot de passe de fuir suite à la décompilation (ne le compilez jamais dans le binaire pour commencer).

Pour plus d'informations sur cette erreur courante, vous pouvez lire l'article CWE-259 . L'article contient une définition plus approfondie, des exemples et de nombreuses autres informations sur le problème.

En Java, l'un des moyens les plus simples de procéder consiste à utiliser la classe Preferences. Il est conçu pour stocker toutes sortes de paramètres de programme, dont certains peuvent inclure un nom d'utilisateur et un mot de passe.

import java.util.prefs.Preferences;

public class DemoApplication {
  Preferences preferences = 
      Preferences.userNodeForPackage(DemoApplication.class);

  public void setCredentials(String username, String password) {
    preferences.put("db_username", username);
    preferences.put("db_password", password);
  }

  public String getUsername() {
    return preferences.get("db_username", null);
  }

  public String getPassword() {
    return preferences.get("db_password", null);
  }

  // your code here
}

Dans le code ci-dessus, vous pouvez appeler la setCredentialsméthode après avoir affiché une boîte de dialogue demandant le nom d'utilisateur et le mot de passe. Lorsque vous devez vous connecter à la base de données, vous pouvez simplement utiliser les méthodes getUsernameet getPasswordpour récupérer les valeurs stockées. Les informations de connexion ne seront pas codées en dur dans vos binaires, la décompilation ne posera donc pas de risque de sécurité.

Remarque importante: les fichiers de préférences ne sont que des fichiers XML en texte brut. Assurez-vous de prendre les mesures appropriées pour empêcher les utilisateurs non autorisés de visualiser les fichiers bruts (autorisations UNIX, autorisations Windows, et cetera). Sous Linux, au moins, ce n'est pas un problème, car l'appel Preferences.userNodeForPackagecréera le fichier XML dans le répertoire personnel de l'utilisateur actuel, qui n'est de toute façon pas lisible par les autres utilisateurs. Sous Windows, la situation peut être différente.

Notes plus importantes: Il y a eu beaucoup de discussions dans les commentaires de cette réponse et d'autres sur ce qu'est l'architecture correcte pour cette situation. La question initiale ne mentionne pas vraiment le contexte dans lequel l'application est utilisée, je vais donc parler des deux situations auxquelles je peux penser. Le premier est le cas dans lequel la personne utilisant le programme connaît déjà (et est autorisée à connaître) les informations d'identification de la base de données. Le second est le cas dans lequel vous, le développeur, essayez de garder les informations d'identification de la base de données secrètes de la personne utilisant le programme.

Premier cas: l'utilisateur est autorisé à connaître les informations de connexion à la base de données

Dans ce cas, la solution que j'ai mentionnée ci-dessus fonctionnera. La Preferenceclasse Java stockera le nom d'utilisateur et le mot de passe en texte brut, mais le fichier de préférences ne sera lisible que par l'utilisateur autorisé. L'utilisateur peut simplement ouvrir le fichier XML des préférences et lire les informations de connexion, mais ce n'est pas un risque de sécurité car l'utilisateur connaissait les informations d'identification au départ.

Deuxième cas: tentative de masquer les informations de connexion de l'utilisateur

C'est le cas le plus compliqué: l'utilisateur ne doit pas connaître les informations de connexion mais doit tout de même accéder à la base de données. Dans ce cas, l'utilisateur exécutant l'application a un accès direct à la base de données, ce qui signifie que le programme doit connaître les informations de connexion à l'avance. La solution que j'ai mentionnée ci-dessus ne convient pas à ce cas. Vous pouvez stocker les informations de connexion à la base de données dans un fichier de préférences, mais l'utilisateur pourra lire ce fichier, car il en sera le propriétaire. En fait, il n'y a vraiment aucun bon moyen d'utiliser ce boîtier de manière sécurisée.

Cas correct: utilisation d'une architecture à plusieurs niveaux

La bonne façon de le faire est d'avoir une couche intermédiaire, entre votre serveur de base de données et votre application client, qui authentifie les utilisateurs individuels et permet d'effectuer un ensemble limité d'opérations. Chaque utilisateur aurait ses propres informations de connexion, mais pas pour le serveur de base de données. Les informations d'identification permettraient d'accéder à la couche intermédiaire (le niveau de logique métier) et seraient différentes pour chaque utilisateur.

Chaque utilisateur aurait son propre nom d'utilisateur et mot de passe, qui pourraient être stockés localement dans un fichier de préférences sans aucun risque de sécurité. C'est ce qu'on appelle une architecture à trois niveaux (les niveaux étant votre serveur de base de données, votre serveur de logique métier et votre application cliente). C'est plus complexe, mais c'est vraiment le moyen le plus sûr de faire ce genre de chose.

L'ordre de base des opérations est:

  1. Le client s'authentifie avec le niveau de logique métier à l'aide du nom d'utilisateur / mot de passe personnel de l'utilisateur. Le nom d'utilisateur et le mot de passe sont connus de l'utilisateur et ne sont en aucun cas liés aux informations de connexion à la base de données.
  2. Si l'authentification réussit, le client envoie une demande au niveau de logique métier pour demander des informations à partir de la base de données. Par exemple, un inventaire de produits. Notez que la requête du client n'est pas une requête SQL; il s'agit d'un appel de procédure à distance tel que getInventoryList.
  3. Le niveau de logique métier se connecte à la base de données et récupère les informations demandées. Le niveau de logique métier est en charge de former une requête SQL sécurisée en fonction de la demande de l'utilisateur. Tous les paramètres de la requête SQL doivent être nettoyés pour empêcher les attaques par injection SQL.
  4. Le niveau de logique métier renvoie la liste d'inventaire à l'application cliente.
  5. Le client affiche la liste d'inventaire à l'utilisateur.

Notez que dans l'ensemble du processus, l'application cliente ne se connecte jamais directement à la base de données . Le niveau de logique métier reçoit une demande d'un utilisateur authentifié, traite la demande du client pour une liste d'inventaire, puis n'exécute qu'une requête SQL.

William Brendel
la source
4
Comment cela empêche-t-il quelqu'un d'obtenir le nom d'utilisateur / mot de passe? Ne pouvez-vous pas simplement le lire à partir du fichier?
Joe Phillips
Comme je l'ai dit dans ma réponse, si vos autorisations de fichier sont correctement définies, seul l'utilisateur exécutant le programme a un accès en lecture à ce fichier de préférences. Dans les environnements UNIX, cela se fait automatiquement. Windows peut nécessiter des étapes supplémentaires (je ne suis vraiment pas sûr, car je n'utilise pas beaucoup Windows).
William Brendel
Je pense que l'idée est que l'utilisateur qui exécute l'application n'est pas celui auquel vous essayez de l'empêcher. Si tel est le cas, vous devrez le chiffrer.
Michael Haren le
Oui, Michael a raison. Essentiellement, l'idée est que vous connaissez déjà le nom d'utilisateur / mot de passe, il n'est donc pas nécessaire de vous le cacher. Cependant, il sera caché aux autres utilisateurs via les autorisations de fichier.
William Brendel
7
Si vous déployez (par exemple) une application d'édition de base de données sur un utilisateur et que vous ne voulez pas qu'il connaisse le nom d'utilisateur et le mot de passe de la base de données, alors vous avez mal conçu la solution et le logiciel client devrait communiquer avec un serveur (via par exemple, un service Web) qui fait les choses DB.
JeeBee
15

Mettez le mot de passe dans un fichier que l'application lira. N'intégrez JAMAIS de mots de passe dans un fichier source. Période.

Ruby a un module peu connu appelé DBI :: DBRC pour une telle utilisation. Je n'ai aucun doute que Java a un équivalent. Quoi qu'il en soit, il n'est pas difficile d'en écrire un.

Keltia
la source
7
Bien que cela facilite légèrement la modification des mots de passe ultérieurement, cela ne résout pas le problème de sécurité de base.
Brian Knoblauch le
Oui. Voir aussi la réponse de William Brendel.
Keltia
1
La méthode que Keltia et moi avons indiquée est la manière acceptée de traiter ce problème. Découpler vos informations de connexion de votre code compilé est l'une des pratiques de sécurité logicielles les plus élémentaires. Mettre les informations de connexion dans un fichier séparé est un moyen efficace d'y parvenir.
William Brendel
De plus, le fait que vos informations de configuration se trouvent dans un fichier en clair doit être annulé par les restrictions du système d'exploitation. Par exemple, sous UNIX, le fichier en clair doit appartenir à l'utilisateur exécutant le programme et disposer des autorisations 0600, de sorte que seul le propriétaire peut le lire.
William Brendel le
4
OK, donc le fichier ne peut être lu que par l'utilisateur exécutant le programme. Génial. Cela ne résout rien. :-) Moi, l'utilisateur dont nous essayons de garder le mot de passe secret, je peux le lire aussi facilement que l'application ...
Brian Knoblauch
3

Êtes-vous en train d'écrire une application Web? Si tel est le cas, utilisez JNDI pour le configurer en externe à l'application. Un aperçu est disponible ici :

JNDI fournit un moyen uniforme pour une application de trouver et d'accéder à des services distants sur le réseau. Le service distant peut être n'importe quel service d'entreprise, y compris un service de messagerie ou un service spécifique à une application, mais, bien entendu, une application JDBC s'intéresse principalement à un service de base de données. Une fois qu'un objet DataSource est créé et enregistré auprès d'un service de dénomination JNDI, une application peut utiliser l'API JNDI pour accéder à cet objet DataSource, qui peut ensuite être utilisé pour se connecter à la source de données qu'il représente.

Tim Howland
la source
le lien fourni est mauvais
James Oravec
2

Quoi que vous fassiez, les informations sensibles seront stockées dans un fichier quelque part. Votre objectif est de le rendre aussi difficile que possible. Ce que vous pouvez réaliser dépend de votre projet, des besoins et de l'épaisseur du portefeuille de votre entreprise.

Le meilleur moyen est de ne stocker aucun mot de passe nulle part. Ceci est réalisé en utilisant des fonctions de hachage pour générer et stocker des hachages de mot de passe:

hash("hello") = 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
hash("hbllo") = 58756879c05c68dfac9866712fad6a93f8146f337a69afe7dd238f3364946366

Les algorithmes de hachage sont des fonctions à sens unique. Ils transforment toute quantité de données en une «empreinte digitale» de longueur fixe qui ne peut pas être inversée. Ils ont également la propriété que si l'entrée change d'un tout petit peu, le hachage résultant est complètement différent (voir l'exemple ci-dessus). C'est idéal pour protéger les mots de passe, car nous voulons stocker les mots de passe sous une forme qui les protège même si le fichier de mots de passe lui-même est compromis, mais en même temps, nous devons être en mesure de vérifier que le mot de passe d'un utilisateur est correct.

Remarque sans rapport: dans l'ancien temps d'Internet, lorsque vous cliquez sur le lien «J'ai oublié mon mot de passe», les sites Web vous envoyaient par e-mail votre mot de passe en texte brut. Ils les stockaient probablement dans une base de données quelque part. Lorsque les pirates avaient accès à leur base de données, ils avaient accès à tous les mots de passe. Étant donné que de nombreux utilisateurs utilisaient le même mot de passe sur plusieurs sites Web, il s'agissait d'un énorme problème de sécurité. Heureusement, de nos jours, ce n'est pas la pratique courante.

Vient maintenant la question: quelle est la meilleure façon de stocker les mots de passe? Je considérerais cette solution (du service d'authentification et de gestion des utilisateurs de Stormpath) comme une sacrément idéale:

  1. Votre utilisateur entre les informations d'identification, et cela est validé par rapport au hachage du mot de passe
  2. Les hachages de mot de passe sont générés et stockés, pas les mots de passe
  3. Les hachages sont effectués plusieurs fois
  4. Les hachages sont générés à l'aide d'un sel généré aléatoirement
  5. Les hachages sont chiffrés avec une clé privée
  6. La clé privée est stockée à un endroit physiquement différent des hachages
  7. Les clés privées sont mises à jour en fonction du temps
  8. Les hachages chiffrés sont divisés en morceaux
  9. Ces morceaux sont stockés dans des emplacements physiquement séparés

Évidemment, vous n'êtes ni Google ni une banque, c'est donc une solution exagérée pour vous. Mais vient ensuite la question: quelle est la sécurité requise pour votre projet, combien de temps et d'argent vous disposez?

Pour de nombreuses applications, bien que non recommandé, le stockage d'un mot de passe codé en dur dans le code peut être une solution suffisante. Cependant, en ajoutant facilement quelques étapes supplémentaires de sécurité à partir de la liste ci-dessus, vous pouvez rendre votre application beaucoup plus sécurisée.

Par exemple, supposons que l'étape 1 ne soit pas une solution acceptable pour votre projet. Vous ne voulez pas que les utilisateurs saisissent le mot de passe à chaque fois, ou vous ne voulez même pas / avez besoin que les utilisateurs connaissent le mot de passe. Vous avez toujours des informations sensibles quelque part et vous souhaitez les protéger. Vous avez une application simple, il n'y a pas de serveur pour stocker vos fichiers ou c'est trop compliqué pour votre projet. Votre application s'exécute sur des environnements dans lesquels il n'est pas possible de stocker des fichiers en toute sécurité. C'est l'un des pires cas, mais avec des mesures de sécurité supplémentaires, vous pouvez avoir une solution beaucoup plus sûre. Par exemple, vous pouvez stocker les informations sensibles dans un fichier et vous pouvez crypter le fichier. Vous pouvez avoir la clé privée de chiffrement codée en dur dans le code. Vous pouvez obscurcir le code, ce qui rend la tâche un peu plus difficile pour quelqu'un de le déchiffrer.ce lien . (Je tiens à vous avertir une fois de plus que ce n'est pas 100% sécurisé. Un hacker intelligent avec les bonnes connaissances et les bons outils peut pirater cela. Mais en fonction de vos exigences et besoins, cela pourrait être une solution suffisante pour vous).

Caner
la source
1

Cette question montre comment stocker des mots de passe et d'autres données dans un fichier chiffré: Cryptage basé sur un mot de passe AES Java 256 bits

Aaron Digulla
la source
1
Quelque part dans le code source encore besoin de déchiffrer le mot de passe crypté pour créer la connexion, ce mot de passe considéré est toujours là.
Bear0x3f
0

MD5 est un algorithme de hachage, pas un algorithme de cryptage, en bref, vous ne pouvez pas récupérer votre hachage, vous ne pouvez que comparer. Il devrait idéalement être utilisé lors du stockage des informations d'authentification de l'utilisateur et non du nom d'utilisateur et du mot de passe de la base de données. db username et pwd doivent être cryptés et conservés dans un fichier de configuration, pour faire le moins.

renégat
la source
J'ai entendu parler de personnes qui génèrent toutes les combinaisons possibles de chaînes et stockent leurs hachages MD5 correspondants. Ainsi, quand ils trouvent le hachage MD5 de quelqu'un, ils trouvent simplement le hachage qu'ils ont stocké et ils obtiennent la chaîne correspondante.
Nav le