Почему BCrypt больше не принимает хэши?

На прошлой неделе я обновил Fedora до новой версии 28, которая вышла с обновлением mongodb до 3.6. См. Как восстановить сервис mongodb после обновления до Fedora 28? за то, как мне удалось решить мою первую проблему, состоящую в том, что mongod больше не запускается. Теперь я столкнулся с другой проблемой в приложении Rails, которое использует эту же базу данных.

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

Таким образом, поскольку при обновлении системы любая попытка входа в этот Rails-проект потерпит неудачу с BCrypt::Errors::InvalidHash in Devise::SessionsController#create ошибка, возникшая при bcrypt (3.1.11) lib/bcrypt/password.rb:60:ininitialize'`. Анализируя далее в консоли проекта Rails, кажется, что любой вызов этого метода потерпит неудачу:

> BCrypt::Password.create('TestPassword')
BCrypt::Errors::InvalidHash: invalid hash
from /home/psychoslave/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/bcrypt-3.1.11/lib/bcrypt/password.rb:60:in `initialize'

Я пытался bundle удалить / переустановить bcryptи даже использовать вместо этого версию gmub-хранилища для гема bcrypt, но это ничего не изменило.

Смотря на /home/psychoslave/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/bcrypt-3.1.11/lib/bcrypt/password.rb:60:ininitialize'`, проблема в том, что хеш недействителен.

# Initializes a BCrypt::Password instance with the data from a stored hash.
def initialize(raw_hash)
  if valid_hash?(raw_hash)
    self.replace(raw_hash)
    @version, @cost, @salt, @checksum = split_hash(self)
  else
    raise Errors::InvalidHash.new("invalid hash")
  end
end

И соответствующий тест выглядит следующим образом:

  def valid_hash?(h)
    h =~ /^\$[0-9a-z]{2}\$[0-9]{2}\$[A-Za-z0-9\.\/]{53}$/
  end

Сам хеш создается через BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)), который в платформе я использую вызов __bc_crypt(secret.to_s, salt), который, кажется, вызывает bcrypt-3.1.11 / ext / mri / bcrypt_ext.c.

Что еще более важно, добавление binding.pry в valid_hash? метод, можно увидеть, что хеш-значение вернулось для вызова BCrypt::Password.create('TestPassword')на самом деле это довольно длинная строка, начало которой кажется обычным, но в итоге получается последовательность, которая, скорее всего, неверно сформирована:

"$2a$10$Eb1f8DSkGh4G1u5GicyTYujBk6SwFXKYCH.nqxapmBlqJ0eFYdX32\x00\x00\x00\x00\xD1F\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00T\xBD\x02\x00\x00\x00\x00\x00\xF1V\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\xE2\xB0\x02\x00\x00\x00\x
00\x00AW\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00 \x04\x00\x00\x00\x00\x00\x00\x86\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\xB5\xF8\x0E\x00\x00\x00\x00\x00q\xD8\x01\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00…"

Я могу предоставить дамп целого хэша, если он может быть интересен (около 32Ko!).

1 ответ

Вот обходное решение, которое делает rspec из bcrypt успешно пройти все тесты

Это действительно ужасный хак, ожидающий правильного решения, но до тех пор выполняет свою работу. Просто поменяй ~/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/bcrypt-3.1.11/lib/bcrypt/engine.rb (путь для адаптации, конечно), строка 51 из:

- __bc_crypt(secret.to_s, salt)
+ __bc_crypt(secret.to_s, salt).gsub(/(\n|\x00).*/, '')

То есть соедините строку, начинающуюся с первого вхождения "\x00" или "\n", если есть.

Кредитное примечание: эта версия взлома была предложена Andrey Sitnik, и я заменил тот, который я предложил здесь самостоятельно, прежде чем обнаруживать его.

После этого BCrypt::Password#create снова заработает:

> BCrypt::Password.create('TestPassword')
=> "$2a$10$YPRnQF3ZihXHpa9kSx7Mpu.j28PlbdwaNs2umSQvAGkS.JJ.syGye"

У меня была эта проблема с (очень) старым приложением и BCrypt 3.1.10, Обновление до 3.1.12 решил проблему.:)

Другие вопросы по тегам