13

I'm switching a site over to rails. It's quite a large site with 50k+ users. The problem is, the existing password hashing method is extremely weak. I have two options:

1) Switch to a new algorithm, generate random passwords for everyone and then email them those passwords and require the change immediately after

2) Implement new algorithm but use the the old one before and then hash the result. For example:

Password: abcdef =Algorithm 1=> xj31ndn =Algorithm 2=> $21aafadsada214

Any new passwords would need to go through the original algorithm (md5) and then have the result of that hashed if that makes any sense? Is there any disadvantage to this?

4

4 回答 4

22

通常不需要重新设置密码,只需等待用户下次登录即可。

  1. 首先尝试使用新算法验证输入的密码。新密码和已转换的密码将不再需要更长的时间进行验证。
  2. 如果不匹配,则将其与旧的哈希算法进行比较。
  3. 如果旧的哈希值匹配,那么您可以计算并存储新的哈希,因为您知道密码。

每个密码存储系统都必须具有切换到更好的哈希算法的选项,您的问题不是一次性迁移问题。像 BCrypt 这样好的密码哈希算法有一个成本因素,有时您必须增加这个成本因素(因为更快的硬件),然后您需要与迁移所需的完全相同的过程。

如果您的第一个算法真的很弱,并且您想立即提供更多保护,那么使用散列旧哈希的选项 2 是一件好事。在这种情况下,您可以计算双哈希并用新的双哈希替换数据库中的旧哈希。

$newHashToStoreInTheDb = new_hash($oldHashFromDb)

您还应该标记此密码哈希(查看原因),以便您可以将其识别为双重哈希。这可以在单独的数据库字段中完成,或者您可以包含您自己的签名。现代密码散列函数还包括算法的签名,以便它们可以升级到更新的算法,并且仍然可以验证旧的散列。该示例显示了 BCrypt 哈希的签名:

$2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
___
 |
 signature of hash-algorithm = 2y = BCrypt

验证将像这样运行:

  1. 确定它是否是双重哈希。
  2. 如果是新的哈希,调用新的哈希函数来验证输入的密码,就完成了。
  3. 如果是双散列,则与双散列算法进行比较new_hash(old_hash($password))
  4. 如果双哈希值匹配,那么您可以计算并存储新的哈希。
于 2013-01-18T15:46:16.297 回答
10

The simplest solution is probably to add a "password hash type" column to the database. Set it initially to "old"; when a user logs in, re-hash the password using the new algorithm and set the database type to "new".

A variant of this method is to store the hash type as part of the hash string. This works just as well, as long as you can unambiguously tell the different hash formats apart, and has the advantage that you can also include any other needed parameters (such as the salt and the work factor for key stretching) in the same string without having to add extra fields for each to your database.

For example, this is the approach typically used by modern Unix crypt(3) implementations (and the corresponding functions in various high-level languages like PHP): a classic DES-based (and horribly weak) password hash would look something like abJnggxhB/yWI, while a (slightly) more modern hash might look like $1$z75qouSC$nNVPAk1FTd0yVd62S3sjR1, where 1 specified the hashing method, z75qouSC is the salt and nNVPAk1FTd0yVd62S3sjR1 the actual hash, and the delimiter $ is chosen because it cannot appear in an old-style DES hash.


The method you suggest, where the new hashes are calculated as:

 hash = new_hash( old_hash( password ) )

can be useful in some cases, since it allows all existing records to be updated without having to wait for users to log in. However, it's only safe if the old hash function preserves enough of the entropy in the passwords.

For example, even a fairly old and weak cryptographic hash function, like unsalted MD5, would be good enough, since its output depends on the entire input and has up to 128 bits of entropy, which is more than almost any password will have (and more than enough to withstand a brute force attack, anyway). On the other hand, trying to apply this construction using the old DES-based crypt(3) function as the old hash would be disastrous, since old crypt(3) would ignore all but the first 8 characters of each password (as well as the most significant bits of even those characters).

于 2013-01-18T16:53:00.267 回答
2

您可以为所有使用新密码方法更新密码的用户创建一个新密码字段,并使用您的选项 2 更新每个人。

结合使用旧密码方法强制所有用户在登录时更新密码将自动将所有活动用户移动到新密码方法。

于 2013-01-18T13:24:43.290 回答
1

另一种方法是在数据库的不同列中保持两个散列可用于迁移阶段:

  • 如果登录时新哈希不存在,请检查旧哈希并保存新哈希并删除旧哈希。
  • 如果新哈希存在,则仅使用它来验证。

因此,一段时间后,您将只剩下新的哈希值 - 至少对于那些至少登录一次的用户而言。

于 2013-01-18T13:25:32.267 回答