Почему 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:in
initialize'`. Анализируя далее в консоли проекта 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:in
initialize'`, проблема в том, что хеш недействителен.
# 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
решил проблему.:)