Шифрование паролей в соответствии с сохраненными зашифрованными паролями. - PullRequest
3 голосов
/ 29 июля 2011

Я начал работать над проектом, в котором уже было много кода. Это приложение Ruby on Rail, которое использует Devise для аутентификации пользователей. Одним из требований приложения является то, что когда пользователь меняет свой пароль, ему не разрешается использовать тот же пароль, что и последние три пароля, которые он использовал ранее. Для этого есть таблица, которая содержит историю паролей для данного пользователя. Эти пароли являются копиями зашифрованных паролей, которые существовали до любой смены пароля пользователем.

Здесь возникает проблема. У нас есть форма смены пароля, которая собирает новый пароль для данного пользователя. Мне нужно иметь возможность взять новый пароль и зашифровать его, чтобы я мог сопоставить зашифрованное значение нового пароля с зашифрованными значениями старых паролей в истории.

Технический материал
Rails версия 3.0.9
Разработать версию 1.3.4
Используя стандартный BCrypt с Devise. bcrypt_ruby версия 2.1.4

Для этого мы переопределяем метод reset_password, поддерживаемый Devise. Это позволяет нам представить наш собственный метод has_repeated_password в пользовательском контроллере.

Ниже приведена версия has_repeated_password, с которой я начал:

  def has_repeated_password?
    return false if self.new_record? || self.version == 1
    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)

    histories.detect do |history|
      history.encrypted_password == self.class.encryptor_class.digest(self.password, self.class.stretches, history.password_salt, self.class.pepper)
    end
  end

Проблема здесь в том, что класс encryptor никогда не определяется, вызывая ошибку при каждом запуске этой подпрограммы. Даже несмотря на то, что есть много примеров, которые утверждают, что это работает, я не могу заставить его работать, когда Devise использует шифрование по умолчанию.

Вторая попытка это следующий код:

  def has_repeated_password?<br>
    return false if self.new_record? || self.version == 1
    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)

    histories.detect do |history|
      pwd = self.password_digest(self.password)
      history.encrypted_password == pwd
    end
  end

В этом случае я никогда не получу пароль, который соответствует какому-либо из сохраненных паролей, хотя я и подтвердил, что пароль в базе данных - это то, что я ожидаю.

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

Любая помощь будет оценена.

1 Ответ

2 голосов
/ 29 июля 2011

Я думаю, что нашел решение своей проблемы. Основным камнем преткновения было то, что я пытался получить зашифрованный пароль, который не был частью пользовательской модели (более) привязанной к Devise. Это решение предполагает, что Devise будет использовать Bcrypt в качестве стандартного инструмента шифрования (не помню, какая версия Devise сделала этот шаг). Bcrypt / Devise фактически скрывает соль для пароля в зашифрованном пароле. Если у вас есть соль и перец, вы можете получить один и тот же пароль для создания одинакового зашифрованного значения.

Итак, вот обновленный код для подпрограммы, указанной выше:

  def has_repeated_password?
    return false if self.new_record? || self.version == 1

    histories = self.versions.find(:all, :order => 'version DESC', :limit => 3)
    histories.detect do |history|
      bcrypt   = ::BCrypt::Password.new(history.encrypted_password)
      password = ::BCrypt::Engine.hash_secret("#{self.password}#{self.class.pepper}", bcrypt.salt)
      password == history.encrypted_password
    end
  end

Ключевым моментом здесь является то, что объект Bcyrpt должен быть создан с существующим зашифрованным паролем, используя ту же соль, что и исходный пароль. Это достигается путем предоставления ему моего сохраненного исторического зашифрованного пароля (history.encrypted_password). Один из других ключевых элементов заключается в том, что пароли истории и предлагаемый новый пароль используют один и тот же перец, которым управляет Devise. Таким образом, используя вызов Engne.has_secret с намеченным новым паролем, его можно сравнить с паролем истории.

Мне пришлось переместить код bcrypt сюда, потому что все методы паролей, поддерживаемые Devise, предполагают, что вы хотите воздействовать на пароль пользователя текущего объекта пользователя.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...