web-dev-qa-db-fra.com

Les mots de passe doivent-ils être réinitialisés automatiquement lorsque la méthode sous-jacente change

Je suis actuellement ingénieur sur un projet en phase de développement. Un "module" sur ce projet donne la possibilité d'authentification/autorisation des utilisateurs. Cependant, nous nous inquiétons du fait que l'algorithme de hachage de mot de passe peut ne pas être à la hauteur des flics (alias pas BCrypt). (La chose terrible n'est pas tout à fait sûre de ce que c'est et d'où ça vient!).

Cela doit évidemment changer et le patch est en cours de planification. Nous devons naturellement mettre à jour tous nos utilisateurs de test parce que leurs mots de passe utiliseront l'ancienne méthode de hachage, ce qui n'est pas vraiment un problème, tous nos utilisateurs de démonstration sont automatisés lors de la construction, il met donc à jour le script. Mais la question suivante est de savoir s'il s'agit d'un système de production avec des utilisateurs actifs et périmés, de tous montants. Quelle serait la meilleure pratique.

  1. Forcer automatiquement une réinitialisation du mot de passe sur chaque utilisateur? Cela informera chaque utilisateur que son mot de passe a été modifié et peut provoquer des questions/confusion et peut faire soupçonner une violation de la sécurité. D'autres questions peuvent être posées auxquelles les parties prenantes du site Web ne pourront pas nécessairement répondre.
  2. Mettez à jour la base de données pour indiquer s'il s'agit de la méthode nouvelle ou ancienne, puis une fois qu'un utilisateur a été authentifié, mettez à jour son mot de passe dans la base de données à l'aide de. Nécessite un peu de logique dans le service et la transition sera sans conséquence pour tout utilisateur existant. Le problème étant qu'il y ait eu une brèche, il peut être évident qu'il y a deux méthodes en cours ici et si la moins sûre se révèle être non sécurisée, elle pourrait évidemment être brisée.
  3. Réinitialisez tous les mots de passe à l'aide d'une version BCrypted du hachage existant. Marquez-le comme l'ancien style, donc lors d'une authentification réussie, il conserve simplement un hachage du mot de passe plutôt qu'un hachage d'un hachage.
94
Crazy Dino

Votre option 1. est une mauvaise idée: en plus des raisons d'expérience utilisateur/relations publiques que vous énoncez, vous donnez également aux attaquants une fenêtre pour intercepter les jetons de réinitialisation de mot de passe et compromettre chaque compte sur votre serveur. Cela ne résout pas non plus votre problème si vous avez même un utilisateur qui est trop paresseux pour se connecter/mettre à jour son mot de passe.

À première vue, les deux 2. et 3. me semblent bien. Votre numéro 2 n'est pas moins sécurisé que ce que vous faites maintenant, mais 2. cela signifierait que vous devez continuer à prendre en charge la connexion faible actuelle pour toujours (ou faire quelque chose comme "Après X mois, nous essuyons votre mot de passe et vous forçons à le faire). une récupération "qui brise la transparence utilisateur de Nice que vous voulez, alors ignorons cela).

Prenons le cas où vous avez des utilisateurs dans la base de données qui ne se reconnecteront plus jamais. Avec les deux 2. et 3. vous devez continuer à prendre en charge l'algorithme de hachage actuel dans votre base de code pour toujours juste au cas où ils se connectent, mais au moins 3. a l'avantage qu'ils (ou plutôt vous) sont protégés contre des attaques par force brute hors ligne si votre base de données est volée.

Étant donné que vous devrez conserver la colonne "indicateur de style ancien" pour toujours, faites-vous une faveur et faites-en un int pas un bool de sorte que si vous devez mettre à jour votre hachage de mot de passe alg encore, vous pouvez enregistrer sur quel ancien style ils se trouvent.


MISE À JOUR: Une question très similaire a été posée ici et construite sur la discussion de ce fil.

89
Mike Ounsworth

Si vous pouvez faire l'option 3, je ne vois pas pourquoi vous envisageriez même les autres. C'est de loin la meilleure option. Avec cette option, mon intuition serait d'envisager d'utiliser deux sels différents, un pour l'ancien algorithme et un pour le nouveau avec bcrypt. J'imagine une configuration comme celle-ci:

  1. Configurez votre nouveau système de mots de passe comme vous le feriez si vous commenciez aujourd'hui.
  2. Créez une nouvelle table séparée contenant des champs pour le nom d'utilisateur (ou l'id), l'algorithme de hachage (le nom ou l'id), le sel.
  3. Lors de la connexion, vérifiez si l'utilisateur a un enregistrement dans le tableau séparé, et si c'est le cas, hachez le mot de passe à l'ancienne, puis hachez le résultat de la nouvelle façon et comparez avec le hachage bcrypt. S'il correspond, re-salez/hachez le mot de passe de la nouvelle façon et supprimez l'enregistrement de la table séparée.

L'inconvénient est que vous devrez conserver le mot de passe en mémoire quelques millisecondes de plus (peu importe) et vous aurez la recherche de table supplémentaire à chaque connexion, pratiquement pour toujours, jusqu'à ce que la table séparée soit vide ou jusqu'à ce que les anciens comptes deviennent assez vicié pour que vous souhaitiez leur demander de réinitialiser leur mot de passe eux-mêmes.

14
TTT

Notez que si votre ancien schéma est haché avec du sel, vous ne pourrez pas utiliser le schéma n ° 3, sauf si vous stockez le sel SÉPARÉMENT.

Normalement, le sel est stocké avec le hachage, et vous utilisez le sel comme entrée pour la fonction de hachage - si vous n'utilisez pas exactement le même sel, vous obtiendrez une sortie complètement différente.

Si vous utilisez newhash (oldsalt + oldhash, newsalt), même avec le mot de passe correct, vous ne pourrez pas recréer oldhash (puisque vous n'avez pas oldsalt) et vous ne pourrez pas générer le hachage final. La même chose s'applique à tout ce qui a des paramètres (par exemple, bcrypt a un paramètre "cost" - cela doit être défini lors du cryptage et est intégré dans la sortie, pour une utilisation lors de la validation du mot de passe).

AUSSI : comme cela a été mentionné par d'autres, si vous stockez que le hachage est de style "ancien" ou "nouveau", pensez plutôt à stocker le "schéma" - où, par exemple 0 est "l'ancien" et 1 est bcrypt (notez que je n'utilise pas "nouveau" - il est "nouveau" maintenant, ne sera pas "nouveau" pour toujours!). Une façon courante de le faire est d'avoir un marqueur au début du hachage (cela peut déjà être le cas!). bcrypt utilise l'un des préfixes standard suivants: "$ 2a $", "$ 2b $", "$ 2x" ou "$ 2y $". Selon les sorties possibles de votre "ancien" algorithme, vous devrez peut-être créer votre propre préfixe pour les marquer, ou vous pourrez peut-être vous en sortir avec "tout ce qui ne commence pas par" $ "est l'ancien algorithme.

Et enfin, puisque vous êtes évidemment préoccupé (à juste titre!) Par la sécurité des anciens mots de passe, je suggère de forcer tout le monde à changer leur mot de passe, en leur envoyant des instructions par jeton (NE PAS! ENVOYER UN LIEN! Vous ne voulez pas vos utilisateurs en cliquant sur un lien! Dites-leur simplement de se connecter à l'endroit habituel). Ensuite, demandez le token ET leur mot de passe. Sinon, quelqu'un qui a volé un mot de passe dans le passé peut changer le mot de passe et obtenir un "nouveau" mot de passe valide.

ENFIN: avoir une date d'expiration - si les mots de passe ne sont pas modifiés à cette date, alors ils doivent être invalidés. Cette date doit être indiquée dans l'e-mail, et pas trop loin dans le futur (une semaine? Dépend du temps que vos clients mettent pour répondre). Après cela, ils devront suivre les procédures de "réinitialisation du mot de passe".

3
AMADANON Inc.

Je ne sais pas quel est votre schéma d'encodage de mot de passe, mais s'il n'est pas trop mauvais, il est probable que la structure du mot de passe dans l'ancien et le nouveau format soit différente.

J'ai déjà vu quelque chose comme ça dans un ancien système BSD lorsque le système est passé d'un encodage de mot de passe traditionnel à un encodage plus sécurisé. Le nouveau a commencé avec une séquence de caractères qui ne pouvait pas exister dans l'ancien schéma, donc chaque fois qu'un utilisateur avec un ancien mot de passe s'est connecté, son mot de passe en texte clair a été validé en utilisant l'ancienne méthode et ré-haché en silence et stocké dans la base de données de mots de passe avec la nouvelle méthode. Après un mois, aucun ancien mot de passe n'était présent dans la base de données, sans que l'utilisateur final ne remarque quoi que ce soit.

Ce serait quelque part entre votre deuxième et troisième méthode.

Je sais que dans un vrai système web de production, les choses peuvent maintenant être bien pires, car les utilisateurs peuvent attendre des semaines voire des mois avant de se reconnecter. Mais (en fonction de l'activité réelle), cela peut être atténué par le fait qu'un utilisateur qui ne s'est pas connecté depuis plusieurs mois peut avoir oublié son mot de passe - ou vous pouvez lui dire qu'il l'a fait ... Cela signifie que j'attendrais un peu plus longtemps ici probablement 3 ou 6 mois et après cette période, je réinitialiserais tous les anciens mots de passe à une valeur interdite forçant l'utilisateur à réinitialiser son mot de passe à la prochaine connexion ... via l'écran mot de passe oublié.

Les bonnes choses ici sont:

  • transparent pour les utilisateurs réguliers
  • aucun changement de schéma de base de données - à condition que le champ de mot de passe accepte les deux styles
  • les utilisateurs occasionnels seront simplement traités comme s'ils avaient oublié leur mot de passe après des mois sans l'utiliser

L'inconvénient est qu'il vous oblige à implémenter simultanément les deux méthodes d'authentification + une mise à jour automatique de tous les mots de passe de style.

2
Serge Ballesta

Vous n'avez pas mentionné la langue que vous utilisez. Php a ses problèmes avec diverses fonctions qui devraient retourner false alors qu'il devrait retourner vrai dans certains cas, ou d'autres fonctions qui semblent faire le travail mais qui manquent de logique pour gérer réellement toutes les entrées possibles valides possibles.

Mais c'est la bonne façon de faire ce dont vous parlez en php même si vous n'utilisez pas php. Le code de haut niveau peut vous laisser un point de départ pour le coder à vos fins.

http://php.net/manual/en/function.password-needs-rehash.php

$password = 'rasmuslerdorf';
$hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';

// The cost parameter can change over time as hardware improves
$options = array('cost' => 11);

// Verify stored hash against plain-text password
if (password_verify($password, $hash)) {
    // Check if a newer hashing algorithm is available
    // or the cost has changed
    if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
        // If so, create a new hash, and replace the old one
        $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
    }

    // Log user in
}

Ce que je ferais serait votre n ° 2 avec ce qui a été mentionné avec l'utilisation d'un int. De la lecture de la documentation sur le PASSWORD_DEFAULT, cela pourrait changer lorsque de meilleurs algorithmes sont trouvés et qu'ils doivent supprimer l'actuel à des fins d'insécurité lors de la mise à niveau de php.

0
Eric