构建 activation/password 重置 link 的最佳方式

Best way to build an activation/password reset link

构建包含激活或密码重置令牌的 link 的最佳方法是什么? URL 将通过电子邮件发送,用户需要点击它来激活他们的帐户或重置他们的密码。对此有很多讨论,但似乎没有一致的方法,从安全和开发的角度来看,每种方法似乎都有优点和缺点:

  1. Link 在查询字符串中包含令牌作为参数和电子邮件地址

    http://www.example.com/activations/:token?email=joe@gmail.com
    

    使用这种方法,令牌在数据库中被散列,电子邮件用于查找用户。然后使用像 brycpt 这样的库将令牌与数据库中的哈希版本进行比较。似乎包括敏感数据,例如查询字符串中的电子邮件地址,exposes some security risks.

  2. Link 仅包含令牌作为参数或查询字符串。

    http://www.example.com/activations/:token
    

    这似乎是理想的解决方案,但我不知道如何比较令牌,除非存储在数据库中的令牌未经过哈希处理。从安全的角度来看,有些人认为 token in the database doesn't need to be hashed, while others argue the token should be hashed。假设我们在数据库中保留一个散列令牌,遍历数据库中的每个令牌并与 link 中的令牌进行比较似乎非常耗时,尤其是在具有大量用户的应用程序中。所以也许我错了,但似乎使用这种方法需要我将令牌存储在未散列的数据库中。

有人对最佳方法有任何想法吗?

没有理由反对使用仅包含令牌的方法 2。

只应将令牌的哈希值存储在数据库中是正确的 (SQL-injection)。您看到的问题来自使用 BCrypt:

  1. Bcrypt 包含一个 salt,它可以防止在数据库中搜索令牌。
  2. 因为 BCrypt 应用 key-stretching 验证非常慢,所以不可能检查每个存储的哈希值。

加盐和key-stretching这两种技术是存储弱密码所必需的,它们使brute-forcing不可行。因为我们的令牌是一个非常强大的 "password"(最少 20 个字符 0-9 a-z A-Z)所以不需要这些技术,您可以将令牌的未加盐的 SHA-256 存储在数据库。这样的hash可以直接用SQL.

搜索

为了证明它真的安全,我们来做个简单的计算。一个 20 个字符的令牌将允许 7E35 组合。如果我们可以计算 3 Giga SHA-256 per second,我们仍然预计需要大约 3'700'000'000'000'000'000 年才能找到匹配项。

只需确保令牌是从加密随机源创建的。