我已经阅读了关于这个主题的一些SO问题,但是对于存储密码的盐渍哈希的应用实践进行了讨论。
让我们从一些基本规则开始:
有几个选项可用于存储密码,但我想考虑一(1):
在数据库中存储带有随机盐的密码哈希值
明文存储的自动失败不公开讨论。 :)在SO和其他地方找到的解决方案是使用MD5 / SHA1并使用双列,两者都有利弊。
MD5 / SHA1很简单。 Java中的MessageDigest提供MD5,SHA1(在现代实现中通过SHA512,当然是1.6)。此外,列出的大多数RDBMS都提供了插入,更新等MD5加密函数的方法。一旦出现“彩虹表”和MD5碰撞(我已经理解了这些概念),问题就变得明显了。
双列解决方案依赖于salt
不需要保密的想法(理解它)。但是,第二列引入了一种复杂性,如果您的遗留系统有一(1)列密码和更新表的代价且代码可能太高,那么这种复杂性可能并不奢侈。
但它是在单个数据库列中存储带有随机盐的密码哈希,我需要通过实际应用更好地理解。
我喜欢这个解决方案有几个原因:预计会有盐,并考虑遗留边界。这是我迷路的地方:如果盐是随机的,并且密码加盐被散列以产生单向存储值,那么系统永远如何匹配明文密码和新随机盐?
我对此有理论,并且当我输入时,我可能正在研究这个概念:给定128字节的随机盐和8字节的密码('foobar12'),可以通过编程方式删除部分作为salt的哈希,通过散列随机的128字节盐并获取原始哈希的子字符串,即哈希密码。然后使用散列算法重新散列匹配...?
所以...任何接受帮助的人。 :)我接近了吗?
答案 0 :(得分:4)
没有什么神秘之处。单列解决方案就像多列解决方案一样,只不过它将salt和hash一起组合成一个列。检查代码仍然必须知道如何将单个值分解为salt和hash。 (这就是密码通常有用的方式 - 例如,UNIX /etc/shadow
格式将算法标识符,salt和hash一起存储在一个字段中。)
你不必过于担心这个问题,因为密码哈希算法应该包含聪明才能做到这一点。例如,如果您使用jBCrypt,那么您只需:
BCrypt.hashpw()
返回的字符串存储在数据库密码列中;和BCrypt.checkpw()
。答案 1 :(得分:1)
你总是要知道盐,将它存储在数据库中(正如你在多列解决方案中看到的那样),或者能够以其他方式生成盐(这会使某些随机点失败,但不是全部。盐)。
如果您只有一列用于存储密码,则可以:
在第一种情况下,你可以提出一些简单的功能:
public String getSalt(String username)
{
// assuming Hash returns a String
return Hash(username + " 1234 my site is totally awesome").substring(0,16);
}
在第二个:
// Passwords stored in the db as 16 characters of salt, and the rest is password hash
public boolean authenticate(String username, String authPassword)
{
// 'SELECT saltyhash FROM users WHERE username=x'
String saltyhash = getSaltyHashForUserFromDB(username);
String salt = saltyhash.substring(0,16);
String dbPassword = salt + Hash(salt + authPassword);
// perform the actual 'SELECT FROM users WHERE saltypassword=x' stuff
return hitTheDatabaseToPerformLogin(username, dbPassword);
}
public void createUser(String username, String password)
{
String salt = createSomeAwesomeSalt();
String saltyhash = salt + Hash(salt + password);
createTheUserInTheDatabase(username, saltyhash);
}
答案 2 :(得分:1)
您还可以在同一列中存储salt和hash(使用分隔符)。
答案 3 :(得分:0)
请参阅http://www.aspheute.com/english/20040105.asp
用户使用盐渍哈希进行身份验证,而不是使用无盐密码或随机盐进行身份验证。盐渍哈希和盐(但不是实际密码)都存储在数据库中(如果您愿意,可以将它们存储在一个列中,但在使用之前必须再将它们分开)。
为了从用户恢复盐渍哈希(以便您可以将其与存储的盐渍哈希进行比较),您需要数据库中的salt和用户提供的密码。
盐渍哈希是这样创建的:
// Initialize the Password class with the password and salt
Password pwd = new Password(myPassword, mySalt);
// Compute the salted hash
// NOTE: you store the salt and the salted hash in the database
string saltedHash = pwd.ComputeSaltedHash();
身份验证是这样完成的:
// retrieve salted hash and salt from user database, based on username
...
Password pwd = new Password(txtPassword.Text, nSaltFromDatabase);
if (pwd.ComputeSaltedHash() == storedSaltedHash)
{
// user is authenticated successfully
}
else
{
...
为每个用户生成一个新的salt。如果两个用户不小心选择了相同的密码,则两个用户帐户的盐渍哈希值仍然不同。
答案 4 :(得分:0)
这是我迷路的地方:如果是盐 是随机的,并随着哈希 密码,系统怎么样 匹配密码?
它通过计算用户使用相同的盐输入的密码的哈希来匹配它,它从数据库中读取(与哈希同时)。