web-dev-qa-db-fra.com

Bcrypt génère des hachages différents pour la même entrée?

Je viens d'ajouter une fonctionnalité d'enregistrement à mon nouveau projet Grails. Pour le tester, je me suis inscrit en donnant un email et un mot de passe. J'utilise l'algorithme bcrypt pour hacher le mot de passe avant de l'enregistrer dans la base de données.

Cependant, lorsque j'essaie de me connecter avec le même e-mail et mot de passe que j'ai donné lors de l'inscription, la connexion échoue. J'ai débogué l'application et découvert que le hachage généré pour le même mot de passe est différent lorsque j'essaie de comparer avec celui déjà haché de la base de données et que la connexion échoue donc (Registration.findByEmailAndPassword (params.email, hashPassd) dans LoginController.groovy renvoie null).

Voici ma classe de domaine Registration.groovy:

class Registration {

   transient springSecurityService

   String fullName
   String password
   String email

   static constraints = {
      fullName(blank:false)
      password(blank:false, password:true)
      email(blank:false, email:true, unique:true)
   }

   def beforeInsert = {
      encodePassword()
   }

   protected void encodePassword() {
      password = springSecurityService.encodePassword(password)
   }
}

Voici mon LoginController.groovy:

class LoginController {

   /**
    * Dependency injection for the springSecurityService.
    */
   def springSecurityService

   def index = {
      if (springSecurityService.isLoggedIn()) {
         render(view: "../homepage")
      }
      else {
         render(view: "../index")
      }
   }

   /**
    * Show the login page.
    */
   def handleLogin = {

      if (springSecurityService.isLoggedIn()) {
         render(view: "../homepage")
         return
      }

      def hashPassd = springSecurityService.encodePassword(params.password)
      // Find the username
      def user = Registration.findByEmailAndPassword(params.email,hashPassd)
      if (!user) {
         flash.message = "User not found for email: ${params.email}"
         render(view: "../index")
         return
      } else {
         session.user = user
         render(view: "../homepage")
      }
   }
}

Voici un extrait de mon Config.groovy disant aux grails d'utiliser l'algorithme bcrypt pour hacher les mots de passe et le nombre de tours de saisie:

grails.plugins.springsecurity.password.algorithm = 'bcrypt'
grails.plugins.springsecurity.password.bcrypt.logrounds = 16
40
adit

Jan a raison - bcrypt par conception ne génère pas le même hachage pour chaque chaîne d'entrée. Mais il existe un moyen de vérifier qu'un mot de passe haché est valide et qu'il est incorporé dans l'encodeur de mot de passe associé. Ajoutez donc une injection de dépendance pour le bean passwordEncoder dans votre contrôleur (def passwordEncoder) Et changez la recherche en

def handleLogin = {

   if (springSecurityService.isLoggedIn()) {
      render(view: "../homepage")
      return
   }

   def user = Registration.findByEmail(params.email)
   if (user && !passwordEncoder.isPasswordValid(user.password, params.password, null)) {
      user = null
   }

   if (!user) {
      flash.message = "User not found for email: ${params.email}"
      render(view: "../index")
      return
   }

   session.user = user
   render(view: "../homepage")
}

Notez que vous ne codez pas le mot de passe pour l'appel isPasswordValid - transmettez le mot de passe en texte clair soumis.

Aussi - complètement sans rapport - c'est une mauvaise idée de stocker l'utilisateur dans la session. Le principal d'authentification est facilement disponible et stocke l'ID utilisateur pour faciliter le rechargement de l'utilisateur selon les besoins (par exemple User.get(springSecurityService.principal.id). Le stockage d'objets Hibernate potentiellement volumineux déconnectés fonctionne très bien en mode dev lorsque vous êtes le seul utilisateur de votre serveur, mais peut être une perte de mémoire importante et vous oblige à contourner les objets déconnectés (par exemple, avoir à utiliser merge, etc.).

38
Burt Beckwith

Un hachage BCrypt inclut sel et par conséquent, cet algorithme renvoie différents hachages pour la même entrée. Permettez-moi de le démontrer en Ruby.

> require 'bcrypt'
> p = BCrypt::Password.create "foobar"
=> "$2a$10$DopJPvHidYqWVKq.Sdcy5eTF82MvG1btPO.81NUtb/4XjiZa7ctQS"
> r = BCrypt::Password.create "foobar"
=> "$2a$10$FTHN0Dechb/IiQuyeEwxaOCSdBss1KcC5fBKDKsj85adOYTLOPQf6"
> p == "foobar"
=> true
> r == "foobar"
=> true

Par conséquent, BCrypt ne peut pas être utilisé pour rechercher des utilisateurs de la manière présentée dans votre exemple. Un autre champ non ambigu doit être utilisé à la place, par ex. le nom ou l'adresse e-mail de l'utilisateur.

23
Jan