ruby: 用盐创建 MD5 校验和?

ruby: create MD5 checksum with salt?

我正在尝试使用 salt 在 ruby 中创建一个 MD5 校验和,但是我找不到使用标准 digest/md5 包来执行此操作的任何方法。

我知道我能做到:

require 'digest/md5'
checksum = '$' + (Digest::MD5.new << plaintext).to_s

但是,似乎没有任何方法可以使用 digest 为这个 MD5 校验和生成指定 salt,而且我还没有找到我可以用于此的任何其他包ruby.

这在 ruby 中甚至可能吗?

提前致谢。

您可以这样添加 compute the digest of multiple chunks

require 'digest/md5'

md5 = Digest::MD5.new
md5 << '$'
md5 << plaintext

checksum = md5.to_s

或者在一个方法调用中通过盐和文本的字符串连接:

salt = '$'
checksum = Digest::MD5.hexdigest("#{salt}#{plaintext}")

我找到了以下内容,它符合我的要求...

https://github.com/mogest/unix-crypt

它是这样工作的:

require 'unix_crypt'
checksum = UnixCrypt::MD5.build(plaintext, salt)

这会生成与 /etc/shadow 中使用的相同的校验和,这正是我想要使用它的目的,

Creating/Validating *nix 样式 MD5 条目加盐

如果您正在尝试管理 *nix 系统密码,最好只使用系统实用程序,而不是构建您自己的实用程序。但是,如果您只想使用 Ruby 核心或标准库功能来生成或验证加盐密码,您当然可以。

计算出的带盐的 MD5 密码通常存储在平面文件数据库中(例如 /etc/shadow),其中 $ 是字段分隔符:

$salt$hashed_pw

请注意,前两个字段以明文形式存储,因为当仅提供密码进行验证时,它们需要重建和散列正确的字符串。因此,您需要将 salt 视为与明文密码分开的变量,尽管在散列时 salt 包含[=36​​=] 与密码。

如果您的实用程序没有字符限制,生成强盐的一种方法是使用 SecureRandom#uuid 生成 UUIDv4 值。例如:

require 'securerandom'

salt = SecureRandom.uuid
#=> "c05280ef-151c-4ebc-83c6-f5f0906f89c2"

然后您可以在 salt + pwpw + salt 上调用您的 MD5 散列,具体取决于您的应用程序的密码实现。例如:

require 'digest/md5'

MD5_STR_FMT = '$%s$%s'.freeze

salt = 'c05280ef-151c-4ebc-83c6-f5f0906f89c2'
pw   = 'plaintext password gathered securely'

pw_digest = Digest::MD5.new << salt + pw
pw_entry  = MD5_STR_FMT % [salt, pw_digest]
#=> "$c05280ef-151c-4ebc-83c6-f5f0906f89c2dcc23c0008e45526e474d0364e4aa5"

然后您将 pw_entry 存储在您的密码数据库文件中,然后在身份验证期间重新计算哈希值时解析出加盐以添加到提供的密码中。例如:

require 'digest/md5'

# this is how we'll validate a password from user
# input against an entry from the password database
def valid_pw? pw, salt, hashed_pw
  pw_digest = Digest::MD5.new << salt + pw
  pw_digest.to_s.eql? hashed_pw.to_s
end

# extract salt and password from a database entry
def parse_pw_entry str
  str.split(?$).slice -2, 2
end

# get this from your password database in whatever
# way you like
pw_entry = '$c05280ef-151c-4ebc-83c6-f5f0906f89c2dcc23c0008e45526e474d0364e4aa5'

# for demonstration purposes only; gather password
# securely from user, then perform your validation
['foo', 'plaintext password gathered securely'].map do |pw|
  valid_pw? pw, *parse_pw_entry(pw_entry)
end
#=> [false, true]