1

我已经阅读了许多关于这个主题的 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'),它可以通过编程方式删除散列的一部分盐,通过散列一个随机的 128 字节盐并获取原始散列的子字符串,即散列密码。然后使用散列算法重新散列以匹配......?

所以......任何接受帮助的人。:) 我很亲密吗?

4

5 回答 5

4

没有什么大秘密。单列解决方案与多列解决方案一样,只是它将盐和哈希组合到一个列中。检查代码仍然必须知道如何将单个值分解为盐和散列。(这就是加盐密码的典型工作方式——例如,UNIX/etc/shadow格式将算法标识符、加盐和散列一起存储在一个字段中)。

不过,您不必担心太多,因为密码散列算法应该包含执行此操作的智能。例如,如果您使用jBCrypt,那么您只需:

  • 存储密码时将返回的字符串存储BCrypt.hashpw()在数据库密码列中;和
  • BCrypt.checkpw()在检查密码时,提供数据库密码列中的值作为第二个参数。
于 2010-05-12T05:21:45.643 回答
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);
}
于 2010-05-12T03:59:51.167 回答
1

您还可以将盐和哈希存储在同一列中(使用分隔符)。

于 2010-05-12T04:03:24.670 回答
0

http://www.aspheute.com/english/20040105.asp

用户使用加盐哈希进行身份验证,而不是未加盐密码或随机加盐本身。加盐哈希和盐(但不是实际密码)都存储在数据库中(如果您愿意,可以将它们存储在单个列中,但在使用前您必须再次将它们分开)。

为了从用户那里恢复加盐哈希(以便您可以将其与存储的加盐哈希进行比较),您需要数据库中的盐和用户提供的密码。

加盐哈希是这样创建的:

  // 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
{
...

为每个用户生成一个新的盐。如果两个用户不小心选择了相同的密码,则两个用户帐户的加盐哈希值仍然不同。

于 2010-05-12T03:47:06.627 回答
0

这就是我迷路的地方:如果盐是随机的并且与密码一起散列,那么系统如何匹配密码?

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

于 2010-05-12T04:07:15.823 回答