7

我正在寻找 Python 应用程序中散列密码的不同替代方案。首先,我决定使用 Flask-bcrypt ( https://github.com/maxcountryman/flask-bcrypt ),但后来决定使用 Argon2。Python 最流行的 Argon2 绑定是 argon2-cffi ( https://github.com/hynek/argon2-cffi )。

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

  • hash散列密码
  • verify将密码与哈希进行比较
  • check_needs_rehash查看是否应在更改散列参数后重新散列密码

有两件事让我困惑。

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

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

4

2 回答 2

17

1)盐是随机的,使用os.urandom. 因此,我想知道该verify方法是否能够以某种方式从哈希中提取盐?

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

>>> 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

该格式在 Argon2 参考实现中进行了总结;也许还有其他参考资料。在这种情况下:

  1. $argon2id$...

    哈希是 Argon2id,这是每个人都应该使用的特定 Argon2 变体(结合了 Argon2i 的侧通道阻力和更难破解的 Argon2d)。

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

    哈希的版本是 0x13(十进制的 19),意思是 Argon2 v1.3,密码哈希竞赛采用的版本。

  3. ...$m=102400,t=2,p=8$...

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

  4. ...$tSm+JOWigOgPZx/g44K5fQ$...

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

  5. ...$WDyus6py50bVFIPkjA28lQ

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

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

如果 Argon2 确实可以从哈希中提取盐,那么在这种情况下使用盐如何更安全,因为获得哈希密码的敌对实体也应该能够提取盐?

salt 的目的是通过简单地对每个用户进行不同的处理来减轻多目标攻击的批量优势。

  • 如果每个人都使用相同的盐,那么攻击者试图找到给定哈希值的$n$密码中的第一个只需要花费大约1 美元/n$的成本,而攻击者试图在给定哈希值的情况下找到单个特定密码所必须花费的成本花费。或者,对手可以通过进行昂贵的预计算(彩虹表)来加速破解个人密码。

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

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

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

一般来说,密码就是秘密:如果有人知道密码,那么他们应该能够登录;如果他们不知道密码,他们应该被带到门口!

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

如果您的密码数据库和您的应用程序之间存在有意义的安全边界,因此攻击者可能会破坏其中一个而不是另一个,那么应用程序可以选择一个统一的随机 32 字节字符串作为密钥,并将其与 Argon2 一起使用使得密码哈希是秘密密码的秘密函数。

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

于 2019-10-16T14:54:47.170 回答
2

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

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

于 2019-10-16T13:35:02.123 回答