web-dev-qa-db-fra.com

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

Java .class les fichiers 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?

81
Jakob Cosoroaba

Ne codez jamais des mots de passe en dur dans votre code. Cela a été soulevé récemment 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 en ingénierie qualifiés. Si le mot de passe est le même sur tous vos logiciels, chaque client devient vulnérable lorsque ce mot de passe devient inévitablement connu. Et parce qu'il est codé en dur, c'est une énorme douleur à corriger.

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

Pour plus d'informations sur cette erreur courante, vous pouvez lire CWE-259 article . 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 pourraient 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 méthode setCredentials 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 getUsername et getPassword pour récupérer les valeurs stockées. Les informations de connexion ne seront pas codées en dur dans vos fichiers binaires, donc la décompilation ne posera pas de risque pour la 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 d'afficher les fichiers bruts (autorisations UNIX, autorisations Windows, etc.). Sous Linux, au moins, ce n'est pas un problème, car appeler Preferences.userNodeForPackage cré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 la bonne architecture pour cette situation. La question d'origine 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 pour la personne utilisant le programme.

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

Dans ce cas, la solution que j'ai mentionnée ci-dessus fonctionnera. La classe Java Preference 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 de préférences et lire les informations d'identification de connexion, mais ce n'est pas un risque pour la sécurité car l'utilisateur connaissait les informations d'identification pour commencer.

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 toujours avoir accès à 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 à l'avance les informations de connexion. La solution que j'ai mentionnée ci-dessus n'est pas appropriée dans ce cas. Vous pouvez stocker les informations d'identification 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 façon correcte de le faire est d'avoir une couche intermédiaire, entre votre serveur de base de données et votre application cliente, 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 pour la sécurité. Ceci est appelé architecture à trois niveaux (les niveaux étant votre serveur de base de données, votre serveur de logique métier et votre application client). 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 le suivant:

  1. Le client s'authentifie avec le niveau 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 d'identification de connexion à la base de données.
  2. Si l'authentification réussit, le client fait une demande au niveau logique métier en demandant des informations à la base de données. Par exemple, un inventaire de produits. Notez que la demande du client n'est pas une requête SQL; il s'agit d'un appel de procédure distante 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 logique métier est chargé 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 logique métier renvoie la liste d'inventaire à l'application cliente.
  5. Le client affiche la liste d'inventaire à l'utilisateur.

Notez que dans tout le 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 et n'exécute alors qu'une requête SQL.

115
William Brendel

Mettez le mot de passe dans un fichier que l'application lira. N'incorporez 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.

15
Keltia

Ecrivez-vous 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 sûr, une application JDBC s'intéresse principalement à un service de base de données. Une fois qu'un objet DataSource est créé et enregistré avec 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.

3
Tim Howland

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

La meilleure façon est de ne stocker aucun mot de passe n'importe où. 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 n'importe quelle 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 mot de passe lui-même est compromis, mais en même temps, nous devons pouvoir vérifier que le mot de passe d'un utilisateur est correct.

Remarque indépendante: Dans les temps anciens d'Internet, lorsque vous cliquez sur "J'ai oublié mon mot de passe" lien, les sites Web vous enverraient votre mot de passe en texte brut. Ils les stockaient probablement quelque part dans une base de données. Lorsque les pirates informatiques avaient accès à leur base de données, ils avaient accès à tous les mots de passe. Étant donné que de nombreux utilisateurs utiliseraient 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 (authentification et service de gestion des utilisateurs de Stormpath) une solution plutôt idéale:

  1. Votre utilisateur entre les informations d'identification, ce qui est validé par le 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é de manière aléatoire
  5. Les hachages sont crypté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

De toute évidence, vous n'êtes ni Google ni une banque, c'est donc une solution exagérée pour vous. Mais vient ensuite la question: quel niveau de sécurité votre projet requiert-il, combien de temps et d'argent vous avez?

Pour de nombreuses applications, bien que cela ne soit pas recommandé, le stockage d'un mot de passe codé en dur dans le code peut être une bonne solution. Cependant, en ajoutant facilement quelques étapes de sécurité supplémentaires dans 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 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 dans des environnements où il n'est pas possible de stocker des fichiers en toute sécurité. C'est l'un des pires cas, mais toujours 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 brouiller le code, ce qui rend un peu plus difficile pour quelqu'un de le casser. Il existe de nombreuses bibliothèques à cet effet, voir ce lien . (Je veux vous avertir une fois de plus que ce n'est pas sûr à 100%. Un pirate intelligent avec les bonnes connaissances et les bons outils peut pirater cela. Mais en fonction de vos exigences et de vos besoins, cela pourrait être une bonne solution pour vous).

2
Caner

Cette question montre comment stocker les mots de passe et autres données dans un fichier crypté: Java 256-bit AES Password-Based Encryption

1
Aaron Digulla

MD5 est un algorithme de hachage, pas un algorithme de chiffrement, bref, vous ne pouvez pas revenir en arrière haché, 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 db. le nom d'utilisateur db et pwd doivent être cryptés et conservés dans un fichier de configuration, pour faire le moins.

0
renegadeMind