如果我只有一个数据库列,如何存储盐渍密码哈希?

时间:2010-05-12 02:49:11

标签: security cryptography passwords salt

我已经阅读了关于这个主题的一些SO问题,但是对于存储密码的盐渍哈希的应用实践进行了讨论。

让我们从一些基本规则开始:

  • 密码,“foobar12”(我们讨论密码的强度)。
  • 一种语言,本讨论的Java 1.6
  • 数据库,postgreSQL,MySQL,SQL Server,Oracle

有几个选项可用于存储密码,但我想考虑一(1):

  

在数据库中存储带有随机盐的密码哈希值

明文存储的自动失败不公开讨论。 :)在SO和其他地方找到的解决方案是使用MD5 / SHA1并使用双列,两者都有利弊。

MD5 / SHA1很简单。 Java中的MessageDigest提供MD5,SHA1(在现代实现中通过SHA512,当然是1.6)。此外,列出的大多数RDBMS都提供了插入,更新等MD5加密函数的方法。一旦出现“彩虹表”和MD5碰撞(我已经理解了这些概念),问题就变得明显了。

双列解决方案依赖于salt不需要保密的想法(理解它)。但是,第二列引入了一种复杂性,如果您的遗留系统有一(1)列密码和更新表的代价且代码可能太高,那么这种复杂性可能并不奢侈。

但它是在单个数据库列中存储带有随机盐的密码哈希,我需要通过实际应用更好地理解。

我喜欢这个解决方案有几个原因:预计会有盐,并考虑遗留边界。这是我迷路的地方:如果盐是随机的,并且密码加盐被散列以产生单向存储值,那么系统永远如何匹配明文密码和随机盐?

我对此有理论,并且当我输入时,我可能正在研究这个概念:给定128字节的随机盐和8字节的密码('foobar12'),可以通过编程方式删除部分作为salt的哈希,通过散列随机的128字节盐并获取原始哈希的子字符串,即哈希密码。然后使用散列算法重新散列匹配...?

所以...任何接受帮助的人。 :)我接近了吗?

5 个答案:

答案 0 :(得分:4)

没有什么神秘之处。单列解决方案就像多列解决方案一样,只不过它将salt和hash一起组合成一个列。检查代码仍然必须知道如何将单个值分解为salt和hash。 (这就是密码通常有用的方式 - 例如,UNIX /etc/shadow格式将算法标识符,salt和hash一起存储在一个字段中。)

你不必过于担心这个问题,因为密码哈希算法应该包含聪明才能做到这一点。例如,如果您使用jBCrypt,那么您只需:

  • 存储密码时,将BCrypt.hashpw()返回的字符串存储在数据库密码列中;和
  • 检查密码时,将数据库密码列中的值作为第二个参数提供给BCrypt.checkpw()

答案 1 :(得分:1)

你总是要知道盐,将它存储在数据库中(正如你在多列解决方案中看到的那样),或者能够以其他方式生成盐(这会使某些随机点失败,但不是全部。盐)。

如果您只有一列用于存储密码,则可以:

  • 动态生成盐(即不是真正随机的,但使用用户名或电子邮件地址的某种功能)
  • 在存储之前以某种方式将salt与哈希密码连接起来。

在第一种情况下,你可以提出一些简单的功能:

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)

  

这是我迷路的地方:如果是盐   是随机的,并随着哈希   密码,系统怎么样   匹配密码?

它通过计算用户使用相同的盐输入的密码的哈希来匹配它,它从数据库中读取(与哈希同时)。