简体   繁体   English

为什么BCrypt不再接受哈希?

[英]Why does BCrypt no longer accept hashes?

Last week I upgrade Fedora to the brand new 28 release, which came with a mongodb upgrade to 3.6. 上周我将Fedora升级到全新的28版本,其中mongodb升级到3.6。 See How to repair mongodb service after an upgrade to Fedora 28? 请参阅升级到Fedora 28后如何修复mongodb服务? for how I managed to resolve my first problem which was that mongod would no longer start. 因为我设法解决了我的第一个问题,即mongod将不再启动。 Now I'm facing an other problem on the Rails application that use this same database. 现在我在使用同一个数据库的Rails应用程序上面临另一个问题。

This most probably is unrelated to the mongodb upgrade, but I thought it might worth providing that context and don't miss a solution for not providing enough of it. 这很可能与mongodb升级无关,但我认为值得提供上下文并且不要错过没有提供足够的上下文的解决方案。

So since the system upgrade any login attempt on this Rails project will fail with a BCrypt::Errors::InvalidHash in Devise::SessionsController#create error, raised at bcrypt (3.1.11) lib/bcrypt/password.rb:60:in initialize'`. 因此,自系统升级以来,对这个Rails项目的任何登录尝试都将失败, BCrypt::Errors::InvalidHash in Devise::SessionsController#create了一个BCrypt::Errors::InvalidHash in Devise::SessionsController#create错误,在bcrypt (3.1.11) lib/bcrypt/password.rb:60:in提出了bcrypt (3.1.11) lib/bcrypt/password.rb:60:in初始化'`。 Analyzing further in a Rails console of the project, it seems any call to this method will fail: 在项目的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'

I tried to bundle uninstall/reinstall bcrypt , and even use the github repository version of the bcrypt gem instead, but it didn't change anything. 我试图bundle卸载/重新安装bcrypt ,甚至使用bcrypt gem的github存储库版本,但它没有改变任何东西。

Looking at /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'`, the problem seems that the hash is not valid. 看看/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'`,the问题似乎哈希无效。

# 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

And the corresponding test is as follow: 相应的测试如下:

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

The hash itself is created through BCrypt::Engine.hash_secret(secret, BCrypt::Engine.generate_salt(cost)) , which in the platform I use call __bc_crypt(secret.to_s, salt) , which seems to be calling bcrypt-3.1.11/ext/mri/bcrypt_ext.c . 哈希本身是通过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

More importantly, adding a binding.pry in the valid_hash? 更重要的是,加入binding.pryvalid_hash? method, it's possible to see what the hash value returned for a call to BCrypt::Password.create('TestPassword') , it's actually a rather long string whose start seems usual, but end up with what is most likely misgenerated sequence: 方法,可以看到为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…"

I can provide a dump of a whole hash if it might be of any interest (around 32Ko!). 如果它可能有任何兴趣(大约32Ko!),我可以提供整个哈希的转储。

Here is a circumvent solution which make rspec of bcrypt pass all tests successfully again. 这是一个规避解决方案,使得bcrypt rspec再次成功通过所有测试。

This is really a uggly hack while waitting a proper solution, but does the job until then. 在等待合适的解决方案时,这真的是一个糟糕的黑客,但直到那时才完成工作。 Just change ~/.rbenv/versions/2.4.3/lib/ruby/gems/2.4.0/gems/bcrypt-3.1.11/lib/bcrypt/engine.rb (path to adapt, of course), line 51 from: 只需更改~/.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).*/, '')

That is, trunk the string starting at the first "\\x00" or "\\n" occurrence, if any. 也就是说,如果有的话,从第一个“\\ x00”或“\\ n”出现开始中继字符串。

Credit note: this version of the hack was proposed by Andrey Sitnik , and I replaced the one I proposed here independently, before discovering it. 信用说明: 此版本的黑客攻击是Andrey Sitnik提出的,在发现它之前,我取代了我在这里独立提出的版本。

After that, BCrypt::Password#create will function again: 之后,BCrypt :: Password#create将再次运行:

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

I had this issue with a (very) old application and BCrypt 3.1.10 . 我有一个(非常)旧的应用程序和BCrypt 3.1.10这个问题。 Upgrading to 3.1.12 resolved the issue. 升级到3.1.12解决了这个问题。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM