2012-10-17 133 views
1

我正在用MySQL创建一个Django网站。我已经决定使用内置的pbkdf2-sha256中的Django和随机生成的salt hash来存储用户的密码。从用户密码安全生成加密密钥?

但是,该网站还需要存储许多其他网站(不使用oauth)的第三方登录凭证。所以我正在研究AES-256加密,当然这个问题成为安全存储加密密钥的地方。

现在这里是我的解决方案:让每个加密密钥=用户实际密码和随机生成的盐的哈希值(与用于存储密码的哈希值不同)。盐将存储在表中,实际的密码和它的散列显然不是。因此,加密密钥将在登录时生成并临时存储,但在注销时到期。进一步说,如果不破解原始的pbkdf2-sha256哈希值,那么会损害服务器的人无法生成加密密钥,即使如此,它只会针对该用户,而不是通用密钥。

缺点是,如果他们更改/重置密码,他们将不得不为每个网站重新输入凭据。但这并不是什么大问题,似乎比在服务器或其他服务器上存储密钥更安全。

但是我只知道24小时前的散列是什么,所以我知道什么。我是否忽略了某些东西,或者这是相当安全的?或者,还有更好的方法?

+0

这就是[PBKDF](http://en.wikipedia.org/wiki/PBKDF2)*是*:基于密码的密钥推导函数。 –

+0

我意识到这一点。这就是为什么我用它来创建单向密钥而不是用户密码。我建议再次使用不同的密码来作为不同登录凭证的单独加密密钥。这样攻击者无法获得加密密钥 –

+0

您可以生成高熵盐,并使用salt +密码上的PBKDF来获得密钥+ IV来加密数据并存储salt和加密数据,这是*长期*存储。在需要时,您可以使用存储的salt和密码(从* where *?中检索)访问加密数据。但是你没有密码。您只在您的用户信息中存储单向散列值。登录表单帖子已经过去了。除非你每次需要时询问用户pwd *,否则你需要将它存储在某处*,这是你的真正问题,*你在哪里存储PWD *? –

回答

3

您提到的算法PBKDF2实际上是为这个明确的目的而设计的。

因此,工作流程将生成一个随机盐。然后将其存储在用户的数据库中。

使用具有较高迭代计数和salt的PBKDF2生成640位密钥材料(80字节)。

的前128位成为密码

的IV下256个​​比特成为密码密钥

最后256位成为MAC密钥(密钥(用于AES-256的密钥)用于验证加密)。

key = PBKDF2-SHA256(password, salt, 50000, 80) 
iv = key[0:128] 
cipherKey = key[128:384] 
macKey = key[384:640] 

然后,使用加密这些密钥(伪码):

ciphertext = AES-256-CBC(data, cipherKey, iv) 
authtext = SHA256-HMAC(ciphertext, macKey) 
result = '{}{}'.format(authtext, ciphertext) 

现在,解密,刚刚散步回来反向...

key = PBKDF2-SHA256(password, salt, 50000, 80) 
iv = key[0:128] 
cipherKey = key[128:384] 
macKey = key[384:640] 

authtext = result[0:32] 
ciphertext = result[32:] 

if !timingSafeComparison(authtext, SHA256-HMAC(ciphertext, macKey)): 
    return false 

return AES-256-CBC-DECRYPT(ciphertext, cipherKey, iv) 

是,如果您的用户忘记密码,则所有加密的数据都将消失。但那就是你想要的,对吧?

+0

这种方法存在的问题是,当您从中拉出超过自然尺寸时,PBKDF2的成本会增加。攻击者可能只对部分输出感兴趣,因为这足以进行确认,从而允许他加快密码搜索的速度,而不是合法的散列器。我更喜欢从PBKDF2输出自然大小,然后使用HKDF生成单个的密钥。 – CodesInChaos