Argon2 库,它在没有秘密的情况下散列密码,并使用看起来不可解析的随机盐

Argon2 library that hashes passwords without a secret and with a random salt that doesn't appear parseable

我正在寻找 Python 应用程序中散列密码的不同替代方案。首先,我选择了 Flask-bcrypt (https://github.com/maxcountryman/flask-bcrypt), but then decided to use Argon2. The most popular Argon2 bindings for Python is argon2-cffi (https://github.com/hynek/argon2-cffi)。

根据其文档 (https://argon2-cffi.readthedocs.io/en/stable/api.html),我需要做的就是使用 3 种方法:

有两件事让我很困惑。

1)盐是随机的,使用os.urandom。因此我想知道 verify 方法是否能够以某种方式从哈希中提取盐?或者换句话说,因为我对盐是什么没有发言权并且无法保存它,所以 verify 方法实际上如何将任何密码与用随机盐散列的密码进行比较?我是否应该自己以某种方式从 hash 的 return 值中解析 salt,并将其与散列值分开存储?或者散列是否应该按原样存储在文档中,保持原样,并且 Argon2 能够以某种方式验证密码?如果 Argon2 确实可以从散列中提取盐,那么在这种情况下使用盐如何更安全,因为获得散列密码的敌对实体也应该能够提取盐?

2) 默认情况下,我不向 hash 方法提供任何秘密,而是密码本身似乎被用作秘密。这安全吗?不为散列方法提供秘密对我来说有什么缺点?

hash的输出实际上是hash、hash参数和salt的编码。您不需要对其进行任何特殊处理,只需正常存储即可。

Argon2 是一种密码哈希算法。它(通常)不需要任何秘密。这在设计上是安全的。除了密码之外,还可以将其与秘密值一起使用,这几乎不会增加任何安全性。也可以将其用作密钥派生函数,这几乎总是很浪费。这些都不会降低安全性,但它们是不必要的,所以请不要打扰。

1) The salt is random, using os.urandom. I thus wonder if the verify method is somehow able to extract the salt from the hash?

hash 方法 returns 一个编码盐、参数和密码哈希本身的字符串,如文档中所示:

>>> from argon2 import PasswordHasher
>>> ph = PasswordHasher()
>>> hash = ph.hash("s3kr3tp4ssw0rd")
>>> hash  
'$argon2id$v=19$m=102400,t=2,p=8$tSm+JOWigOgPZx/g44K5fQ$WDyus6py50bVFIPkjA28lQ'
>>> ph.verify(hash, "s3kr3tp4ssw0rd")
True

格式汇总in the Argon2 reference implementation;也许还有其他参考资料。在这种情况下:

  1. $argon2id$...

    hash为Argon2id,是大家应该使用的Argon2具体变种(结合了Argon2i的侧信道阻力和更难破解的Argon2d)。

  2. ...$v=19$...

    散列版本为0x13(十进制19),表示Argon2 v1.3,Password Hashing Competition.

  3. 采用的版本
  4. ...$m=102400,t=2,p=8$...

    内存使用为100 MB (102400 KB),时间为2次迭代,并行度为8路。

  5. ...$tSm+JOWigOgPZx/g44K5fQ$...

    盐是tSm+JOWigOgPZx/g44K5fQ(base64),或b5 29 be 24 e5 a2 80 e8 0f 67 1f e0 e3 82 b9 7d(十六进制)。

  6. ...$WDyus6py50bVFIPkjA28lQ

    密码哈希本身是 WDyus6py50bVFIPkjA28lQ (base64),或 58 3c ae b3 aa 72 e7 46 d5 14 83 e4 8c 0d bc 95(十六进制)。

verify 方法获取此字符串和候选密码,使用所有编码参数重新计算密码哈希,并将其与编码密码哈希进行比较。

And if indeed Argon2 can extract the salt out of the hash, how is using a salt any safer in that case since a hostile entity who gets a hashed password should then also be able to extract the salt?

加盐的目的是通过对每个用户不同来减轻多目标攻击的批量优势。

  • 如果每个人都使用 相同的 盐,那么对手会试图找到 $n$[ 的 first =112=] 密码 给定的哈希只需要花费 $1/n$ 对手试图找到 单个特定密码[的成本 鉴于其散列将不得不花费。或者,对手可以通过进行昂贵的预计算(彩虹表)来加速破解 个人 密码。

  • 但是如果每个人都使用不同的盐,那么批量优势或预计算优势就会消失。

在 32 字节字符串中随机统一选择 salt 只是保证每个用户都有不同 salt 的简单方法。原则上,可以想象一个权威机构向世界上的每个人分发一个连续的数字作为他们的 Argon2 盐,但该系统的扩展性不是很好——我不仅仅意味着 你的应用程序 可以使用计数权限,但是 世界上的每个应用程序 都必须使用 相同的 计数权限,我认为计数是在芝麻街太忙了,没空做这份工作。

2) By default I do not supply any secret to the hash method and instead the password itself seems to be used as a secret. Is this secure? What are the downsides for me not supplying a secret to the hashing method?

通常密码 秘密:如果有人知道密码,那么他们应该能够登录;如果他们不知道密码,他们应该被带到门口!

也就是说,Argon2 支持密钥,它与盐和密码分开。

如果您的 密码数据库 和您的 应用程序 之间存在有意义的安全边界,那么对手可能会妥协但不会另一个,然后 application 可以选择一个统一的随机 32 字节字符串作为密钥,并将其与 Argon2 一起使用,这样密码哈希就是 secret秘密密码的功能。

那样的话,转储密码数据库而不是应用程序密钥的对手甚至无法测试猜测密码,因为他们不知道计算密码哈希所需的密钥。

有点晚了,但是 pyargon2 是克服这个问题的有效替代方案。首先安装 repo:

pip install pyargon2

然后使用:

from pyargon2 import hash
password = 'a strong password'
salt = 'a unique salt'
hex_encoded_hash = hash(password, salt)

更多信息:

https://github.com/ultrahorizon/pyargon2

来源:https://github.com/jwsi