On me présente le scénario suivant:
J'ai une base de données MySQL avec une table d'utilisateurs. Le tableau comporte deux champs: username
et password
. Le mot de passe est stocké sous forme de hachage non salé.
Une application de plus de 15 ans utilise cette base de données pour authentifier l'accès à ses services. Il n'est accessible qu'en interne.
Notre équipe est chargée de moderniser ces services en proposant une nouvelle application en suivant les meilleures pratiques et les enseignements tirés des 15 dernières années (et potentiellement plus qui ont été ignorés dans l'application d'origine). Cette application est accessible à partir du large Internet ouvert.
Nous sommes censés réutiliser la base de données actuelle à des fins d'authentification pour permettre aux deux applications de s'exécuter en parallèle sur la même base de données d'authentification sous-jacente.
J'ai exprimé plusieurs préoccupations concernant la sécurité, car cette application concerne la gestion des données personnelles de mes collègues et de moi.
Nous avons décidé qu'une partie de notre modernisation est que des politiques de mot de passe sont mises en place et que les mots de passe sont stockés à la fois salés et poivrés.
Cela signifie que notre table de base de données obtiendrait un troisième champ salt
et le champ password
stocke maintenant notre hachage salé et poivré.
Le problème ici est que cela cassera l'authentification dans notre ancienne application. Comme le code est très ancien et que nous n'avons même plus de compilateur fonctionnel, il est hors de question de changer le code de l'application d'origine.
Ma question est donc:
Vous avez des exigences contradictoires ici. Les exigences de compatibilité vous obligent à conserver les anciens hachages. Les exigences de sécurité vous obligent à les supprimer. Vous devrez faire un choix ici sur les exigences à remplir.
Si vous décidez de conserver la compatibilité descendante, essayez de tirer le meilleur parti d'une mauvaise situation:
Ou, alternativement, si vous n'avez pas peur de créer un peu de gâchis:
Existe-t-il un moyen sûr d'ajouter du sel (et du poivre) à notre base de données d'authentification tout en conservant la capacité de l'ancienne application à authentifier les utilisateurs?
Oui, cela peut être fait. Vous trouverez ci-dessous des instructions de mise en œuvre de haut niveau. La technique de base consiste à hacher tous les mots de passe, puis à MITM la connexion entre le client et le serveur hérité afin de remplacer le mot de passe non haché par un mot de passe haché. Notez que vous devrez proposer un plan de déploiement; exécuter aveuglément l'étape 1 de la production va tout casser.
Étape 1: Salez tous les mots de passe existants, puis stockez le sel quelque part. Remplacez le champ de mot de passe de la base de données héritée par le mot de passe salé.
Étape 2: Créez une cale. Le shim acceptera des paramètres identiques à ceux des API héritées.
Donc, si l'API héritée est implémentée comme:
LegacyDoStuff(username,password,argument)
{
if(!VerifyCredentials(username,password)) return AuthenticationError();
result = DoStuff(argument);
return result;
}
La nouvelle API est implémentée comme:
NewDoStuff(username,password,argument)
{
hashedpassword = DoHash(password+getSalt(username))
return LegacyDoStuff(username,hashedpassword,argument);
}
Étape 3: Pointez le client hérité sur le shim, au lieu du serveur principal (ou de manière équivalente, déplacez le serveur hérité vers le nouveau IP/DNS, puis placez le shim sur l'ancien IP/DNS).
Cette approche vous permet de traiter les éléments internes du code hérité comme une boîte noire, mais elle vous oblige à être conscient de la surface publique du code hérité, car votre cale devra envoyer des demandes/réponses entre le client et le serveur hérité.
Cette approche, contrairement aux approches décrites dans d'autres réponses, évite complètement de stocker l'ancien mot de passe. Cependant, cette approche est beaucoup plus difficile à faire et est beaucoup plus susceptible d'introduire des bogues.
Existe-t-il un moyen sécurisé d'ajouter du sel (et du poivre) à notre base de données d'authentification tout en conservant la capacité de l'ancienne application à authentifier les utilisateurs?
Non. La raison du salage et du hachage des mots de passe est que si la base de données utilisateur est piratée/divulguée/compromise, les mots de passe des utilisateurs ne sont pas accessibles à l'attaquant (voir Quel est l'intérêt du hachage des mots de passe? ). Dans la solution que vous décrivez, les mots de passe des utilisateurs sont toujours stockés dans la base de données des utilisateurs, dans une colonne adjacente, en texte clair. Cela va complètement à l'encontre de l'objectif de salage et de hachage des mots de passe.
Il est clair que la table actuelle doit rester pour que l'ancienne application puisse fonctionner.
Ajoutez peut-être une nouvelle table avec des hachages salés + poivrés pour la nouvelle application à utiliser, puis:
Cela suppose que le système de contrôle des mots de passe est distinct de l'un ou l'autre des systèmes de contenu.
Cela signifie que toute vulnérabilité dans le nouveau système ne peut pas accéder aux anciens hachages (une fois migrés). La base de données pourrait avoir une vulnérabilité qui permet aux informations d'identification de la base de données du nouveau système d'avoir accès à une table, ce qu'elle ne devrait pas, mais c'est beaucoup moins probable que votre nouvelle application présente une vulnérabilité.
Finalement, soit tout le monde s'est connecté au moins une fois et a eu ses hachages mis à jour de manière transparente (il n'y a plus de sels NULL dans le tableau), soit les seuls qui restent ne sont jamais connectés.
Mettre les hachages non salés et salés les uns à côté des autres dans le tableau illustre simplement le fait que tant que le système hérité continuera d'être utilisé, l'utilisation des meilleures pratiques dans le nouveau système restera non pertinente du point de vue de la sécurité .
Oui, c'est possible.
Dans la nouvelle application, ayez un algorithme de hachage double de hachage de hachage, par exemple: étape 1: hachage du mot de passe salé, étape 2: hachage de ce hachage (pas de sel)
Approche n ° 1:
Développez une application autonome simple qui prend un mot de passe et génère le hachage salé d'un mot de passe utilisateur et s'affiche à l'écran ou en silence dans le presse-papiers.
Demandez à tous les utilisateurs internes de réinitialiser leur mot de passe dans l'ancienne application à la sortie de l'application autonome, et à partir de maintenant lors de la connexion, utilisez le processus de saisie du mot de passe dans l'application autonome pour générer le hachage du mot de passe qui sera entré dans la connexion écran. La sortie pourrait être générée dans le presse-papiers pour éviter les indices visuels.
Approche n ° 2
Si vous pouvez en quelque sorte utiliser la base de données, faites le hachage de hachage dans la base de données. Faites à l'étape 2 le hachage salé et ne le stockez que.
Modifier pour clarifier: exemple: si vous utilisez une base de données qui prend en charge des opérateurs personnalisés, déclarez un opérateur d'égalité personnalisé sur une colonne stockant le newsaltedhash (oldhash) en tant que type personnalisé. Lorsque l'application exécute une requête pour une ligne avec un nom d'utilisateur correspondant et oldhash, l'opérateur personnalisé exécutera newsaltedhash sur le hachage entrant et effectuera la comparaison. La réinitialisation du mot de passe est gérée par des déclencheurs d'insertion.
Résumé: Dans les deux scénarios, vos applications fonctionnent comme prévu à l'origine et le hachage de mot de passe stocké est un hachage double + sel. (Pepper ajouté en option dans l'une ou l'autre implémentation)
Cette réponse est inspirée par réponse de Brian et approche de Frank # 1 qui utilisent tous deux essentiellement le nouveau hachage salé comme mot de passe pour l'ancienne application. Étant donné que cela nécessite qu'une application récupère le sel de la base de données pour générer le hachage, il serait plus simple et plus sûr que cette application authentifie l'utilisateur et utilise un jeton aléatoire complètement séparé comme mot de passe pour l'ancien système.
Choisissez une bibliothèque de hachage de mot de passe bien prise en charge pour la langue de votre choix, qui devrait gérer la génération de sel sécurisé pour vous. Il produira probablement une seule chaîne contenant le hachage, le sel et d'autres paramètres nécessaires pour vérifier le mot de passe, vous permettant de déployer progressivement un algorithme plus puissant à l'avenir. Par exemple, dans le cas de PHP, utilisez les fonctions de hachage de mot de passe intégrées .
Créez une nouvelle colonne dans la base de données pour stocker cette chaîne unique. Une façon de remplir cela initialement est de calculer NewHashFunction(OldStoredHash)
, puis d'exécuter NewVerifyFunction(NewStoredHash, OldHashFunction(UserEnteredPassword))
. Il existe de nombreuses discussions à ce sujet, et cela ne fait aucune différence pour le reste de cette réponse.
Essuyez définitivement tous les anciens hachages non sécurisés, mais ne laissez pas tomber la colonne. À ce stade, personne ne peut se connecter à l'ancienne application, nous devons donc y remédier ...
Créez une page dans votre nouvelle application (où l'utilisateur est déjà authentifié) ou une page autonome (qui authentifie l'utilisateur à l'aide de la nouvelle bibliothèque de mots de passe) qui fait ce qui suit:
Notez que ce mot de passe aléatoire n'a pas besoin d'être stocké n'importe où, car si l'utilisateur veut se reconnecter, il peut simplement en générer un nouveau.
Les mots de passe aléatoires sont forcément beaucoup plus forts que la plupart des utilisateurs ne l'auraient défini à moins d'utiliser un gestionnaire de mots de passe, mais ils seront toujours stockés non salés et probablement à l'aide d'un hachage rapide. Il existe également un risque que le mot de passe soit copié quelque part lorsqu'il est montré à l'utilisateur. Nous pouvons rendre les deux attaques beaucoup plus difficiles en n'ayant que les mots de passe aléatoires valides pour une courte période de temps.
Ajoutez une colonne supplémentaire à la base de données pour la "date d'expiration du mot de passe aléatoire". Cela peut être extrêmement court s'il se connecte automatiquement et quelques minutes si l'utilisateur doit saisir lui-même le mot de passe. Un travail planifié doit ensuite s'exécuter une fois par minute et effacer l'ancien champ de mot de passe pour toutes les lignes qui ont expiré.