Bcrypt генерирует разные хеши для одного и того же ввода? - PullRequest
32 голосов
/ 12 декабря 2011

Я только что добавил функцию регистрации в свой новый проект Grails. Для тестирования я зарегистрировался, указав адрес электронной почты и пароль. Я использую алгоритм bcrypt для хеширования пароля перед сохранением его в базе данных.

Однако, когда я пытаюсь войти в систему с тем же адресом электронной почты и паролем, которые я указал при регистрации, происходит сбой входа. Я отладил приложение и обнаружил, что хэш, сгенерированный для того же пароля, отличается, когда я пытаюсь сравнить его с уже хэшированным из базы данных, и, следовательно, происходит сбой входа в систему ( Registration.findByEmailAndPassword (params.email, hashPassd) ) в LoginController.groovy возвращает ноль ).

Вот мой класс домена 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)
   }
}

Вот мой 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")
      }
   }
}

Вот фрагмент из моего Config.groovy, в котором говорится о том, чтобы использовать алгоритм bcrypt для хэширования паролей и количества циклов ввода:

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

Ответы [ 2 ]

35 голосов
/ 12 декабря 2011

Ян верен - bcrypt по конструкции не генерирует одинаковый хеш для каждой входной строки. Но есть способ проверить правильность хешированного пароля и включить его в соответствующий кодировщик паролей. Поэтому добавьте внедрение зависимостей для компонента passwordEncoder в ваш контроллер (def passwordEncoder) и измените поиск на

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")
}

Обратите внимание, что вы не кодируете пароль для вызова isPasswordValid - передайте пароль в открытом виде.

Также - совершенно не связано - плохая идея хранить пользователя в сеансе. Принципал Auth доступен и хранит идентификатор пользователя, чтобы упростить его перезагрузку по мере необходимости (например, User.get(springSecurityService.principal.id). Хранение отключенных потенциально больших объектов Hibernate отлично работает в режиме разработки, когда вы являетесь единственным пользователем вашего сервера, но может быть значительной тратой памяти и вынуждает вас обходить отключаемые объекты (например, нужно использовать merge и т. д.).

19 голосов
/ 12 декабря 2011

Хеш BCrypt включает соль , и в результате этот алгоритм возвращает разные хэши для одного и того же входа. Позвольте мне продемонстрировать это в 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

Следовательно, BCrypt нельзя использовать для поиска пользователей способом, представленным в вашем примере. Вместо этого следует использовать альтернативное однозначное поле, например, имя пользователя или адрес электронной почты.

...