Vous maintenez une application existante avec une base d'utilisateurs établie. Au fil du temps, il est décidé que la technique de hachage de mot de passe actuelle est obsolète et doit être mise à niveau. En outre, pour des raisons UX, vous ne voulez pas que les utilisateurs existants soient obligés de mettre à jour leur mot de passe. La mise à jour de hachage de mot de passe entière doit se produire derrière l'écran.
Supposons un modèle de base de données "simpliste" pour les utilisateurs contenant:
Comment fonctionne-t-on pour résoudre une telle exigence?
Mes pensées actuelles sont:
Cela me laisse avec le problème que je ne peux pas différencier raisonnable entre les utilisateurs qui ont et ceux qui n'ont pas mis à jour leur hachage de mot de passe et seront donc forcés de vérifier les deux. Cela semble horriblement imparfait.
En outre, cela signifie fondamentalement que la vieille technique de hachage pourrait être forcée de rester indéfiniment jusqu'à ce que chaque utilisateur ait mis à jour leur mot de passe. Seulement à ce moment-là pourrais-je commencer à retirer l'ancien chèque de hachage et retirez le champ de base de données superflu.
Je cherche principalement des conseils de conception ici, car ma "solution" actuelle est sale, incomplète et quelle que soit la netteté, mais si le code réel est nécessaire pour décrire une solution possible, n'hésitez pas à utiliser n'importe quelle langue.
Je suggérerais d'ajouter un nouveau champ "Hash_Method", avec peut-être un 1 pour signifier l'ancienne méthode et une 2 pour signifier la nouvelle méthode.
Raisonnablement parlant, si vous vous souciez de ce genre de chose et que votre demande est relativement de longue durée (qu'elle apparaît apparemment déjà), cela va probablement se reproduire lorsque la cryptographie et la sécurité de l'information sont un champ aussi évolué et plutôt imprévisible. Il y avait un moment où une simple course à travers MD5 était standard, si le hachage a été utilisé du tout! Ensuite, on pourrait penser qu'ils devraient utiliser SHA1, et il y a maintenant salage, sel global + sel aléatoire individuel, SHA3, différentes méthodes de génération de nombres aléatoires de crypto-prêt ... Cela ne va pas simplement "arrêter", donc vous pourriez aussi Bien réparer cela de manière extensible et répétable.
Donc, disons maintenant que vous avez quelque chose comme (à Pseudo-JavaScript pour la simplicité, j'espère):
var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword());
if (user.getPasswordHash() == tryPassword)
{
// Authenticated!
}
function hashPassword(clearPassword)
{
// TODO: Learn what "hash" means
return clearPassword + "H@$I-I";
}
Réalisant maintenant qu'il y a une meilleure méthode, il vous suffit de donner un refactoring mineur:
var user = getUserByID(id);
var tryPassword = hashPassword(getInputPassword(), user.getHashingMethod());
if (user.getPasswordHash() == tryPassword)
{
// Authenticated!
}
function hashPassword(clearPassword, hashMethod)
{
// Note: Hash doesn't mean what we thought it did. Oops...
var hash;
if (hashMethod == 1)
{
hash = clearPassword + "H@$I-I";
}
else if (hashMethod == 2)
{
// Totally gonna get it right this time.
hash = SuperMethodTheNSASaidWasAwesome(clearPassword);
}
return hash;
}
Aucun agents secrets ou programmeurs n'a été blessé dans la production de cette réponse.
Si vous vous souciez de déployer le nouveau schéma de hachage à tous les utilisateurs le plus rapidement possible (par exemple, parce que l'ancien est vraiment insécurisé), il existe en fait un moyen de "migration" instantanée de chaque le mot de passe.
L'idée est fondamentalement à hachage le hash. Plutôt que d'attendre que les utilisateurs fournissent leur mot de passe existant (p
) lors de la connexion suivante, vous utilisez immédiatement le nouvel algorithme de hachage (H2
) sur le HASH existant déjà produit par l'ancien algorithme (H1
):
hash = H2(hash) # where hash was previously equal to H1(p)
Après cette conversion, vous êtes toujours parfaitement capable d'effectuer une vérification de mot de passe; Il vous suffit de calculer H2(H1(p'))
au lieu de la précédente H1(p')
Chaque fois que l'utilisateur tente de vous connecter avec le mot de passe p'
.
En théorie, cette technique pourrait être appliquée à plusieurs migrations (H3
, H4
, etc.). En pratique, vous voudriez vous débarrasser de l'ancienne fonction de hachage, à la fois pour des raisons de performance et de lisibilité. Heureusement, cela est assez facile: lors de la prochaine connexion réussie, calculez simplement le nouveau mot de passe de l'utilisateur et remplacez le hachage de hachage existant avec celui-ci:
hash = H2(p)
Vous aurez également besoin d'une colonne supplémentaire pour mémoriser ce que vous stockez: celui du mot de passe ou de l'ancien hachage. Dans l'événement malheureux de la future de base de données, cette colonne ne devrait pas faciliter le travail du cracker; Quelle que soit sa valeur, l'attaquant devra toujours inverser l'algorithme Secure H2
plutôt que l'ancien H1
.
Votre solution (une colonne supplémentaire dans la base de données) est parfaitement acceptable. Le seul problème avec c'est celui que vous avez déjà mentionné: que l'ancien hachage est toujours utilisé pour les utilisateurs qui n'ont pas authentifié depuis le changement.
Afin d'éviter cette situation, vous pouvez attendre que vos utilisateurs les plus actifs passent à la nouvelle algorithme de hachage, puis:
Supprimer les comptes des utilisateurs qui n'ont pas authentifié depuis trop longtemps. Il n'y a rien de mal à supprimer un compte d'un utilisateur qui n'est jamais venu sur le site Web depuis trois ans.
Envoyez un email aux utilisateurs restants parmi ceux qui n'ont pas authentifié pendant un certain temps, en disant qu'ils peuvent être intéressés par quelque chose de nouveau. Cela contribuera à réduire davantage le nombre de comptes qui utilisent le hachage plus âgé.
Soyez prudent: si vous avez une option sans spam, les utilisateurs peuvent vérifier sur votre site Web, n'envoyez pas le courrier électronique aux utilisateurs qui l'ont vérifié.
Enfin, quelques semaines après l'étape 2, détruisez le mot de passe des utilisateurs qui utilisent toujours l'ancienne technique. Quand et s'ils essaient d'authentifier, ils verront que leur mot de passe est invalide; S'ils sont toujours intéressés par votre service, ils pourraient la réinitialiser.
J'ai fait quelque chose comme ça et il y a un moyen de dire si c'est le nouveau hachage et le rend encore plus sûr. Je suppose que plus de sécurité est une bonne chose pour vous si vous prenez le temps de mettre à jour votre hachage ;-)
Ajoutez une nouvelle colonne à votre table de base de données appelée "sel" et bien, utilisez-la comme sel généré aléatoire par utilisateur. (Presque empêche les attaques de table arc-en-ciel)
De cette façon, si mon mot de passe est "PASS123" et que le sel aléatoire est 3333, mon nouveau mot de passe serait le hachage de "PASS1233333".
Si l'utilisateur a un sel, vous savez que c'est le nouveau hachage. Si le sel de l'utilisateur est null, vous savez que c'est l'ancien hachage.
Quelle que soit la qualité de votre stratégie de migration, si la base de données a déjà été volée (et que vous ne pouvez pas prouver négativement que cela ne s'est jamais produit), les mots de passe stockés avec des fonctions de hachage non sécurisés peuvent déjà être compromis. La seule atténuation pour cela oblige les utilisateurs à modifier leurs mots de passe.
Ce n'est pas un problème qui peut être résolu (uniquement) en écrivant le code. La solution consiste à informer les utilisateurs et les clients des risques qu'ils ont été exposés. Une fois que vous êtes prêt à être ouvert à ce sujet, vous pouvez faire la migration en réinitialisant simplement le mot de passe de chaque utilisateur, car c'est la solution la plus simple et la plus sécurisée. Toutes les alternatives sont vraiment de cacher le fait que vous avez bousillé.