随着最近(例如LinkedIn)关于密码的所有讨论,我正在研究密码散列实现。在喝了两杯咖啡和早上阅读之后,我不再是一个密码学家了,就像我刚开始时一样。我真的不想假装我是。
具体问题
使用整数唯一用户 ID 作为有效盐会失败吗?(crypt() 只使用 16 位?)
如果我只是在哈希上一遍又一遍地运行 sha256() 直到用完一秒钟,这是否会击败蛮力攻击?
如果我必须问这些问题,我应该使用 bcrypt 吗?
讨论/解释:
目标很简单,如果我的用户的散列密码被泄露,他们:
- 不会“容易”破解,
- 破解一个密码不会暴露使用相同密码的其他用户)。
我读到的#1 是哈希计算必须很昂贵——比如说,需要一两秒来计算,并且可能需要一点或内存(以阻止硬件解密)。
bcrypt 内置了这个,如果我理解正确的话,scrypt 更具前瞻性,并且包括最低内存使用要求。
但是,通过“重新散列” sha256() 的结果多次以用完几秒钟,然后将最终循环计数与散列一起存储以供以后检查提供的密码,这是否是一种同样有效的消磨时间的方法?
对于#2,为每个密码使用唯一的盐很重要。尚不清楚盐必须有多随机(或大)。如果目标是避免使用“mypassword”作为密码的每个人都拥有相同的哈希值,那么仅仅这样做还不够吗?:
hash = sha256_hex( unique_user_id + user_supplied_password );
甚至这个,虽然我不确定它能给我带来什么:
hash = sha256_hex( sha256( unique_user_id ) + user_supplied_password );
除了我知道它是唯一的之外,我可以从使用用户 ID 中看到的唯一好处是避免将盐与哈希一起保存。没有太大的优势。使用用户 ID 作为盐是否存在真正的问题?它没有完成#2吗?
我假设如果有人可以窃取我用户的散列密码,那么我必须假设他们可以获得他们想要的任何东西——包括生成散列的源代码。那么,在散列之前向密码添加一个额外的随机字符串(相同的字符串)有什么好处吗?那是:
# app_wide_string = one-time generated, random 64 7-bit *character* string.
hash = sha256_hex( unique_user_id + app_wide_string + user_supplied_password );
我已经看到了这个建议,但我不明白我从每个用户的盐中获得了什么。如果有人想暴力破解攻击,他们会知道“app_wide_string”并在运行字典攻击时使用它,对吧?
如上所述,是否有充分的理由使用 bcrypt 而不是我自己的滚动?也许我问这些问题的事实就足够了?
顺便说一句——我刚刚在我的笔记本电脑上计时了一个现有的散列函数,我每秒可以生成大约 7000 个散列。不完全是通常建议的一两秒。
一些相关链接: