其他两个答案似乎表达了一种关于使用 HMAC 签名令牌进行密码重置的 FUD。
如果正如其他答案所述,担心是关于从服务器窃取您的唱歌密钥,那么攻击者已经可以访问的不仅仅是签名密钥,此时这个论点是没有意义的。
将令牌数据保存在数据库中并不是比使用签名令牌更好(或更差)的解决方案。两者都是合理的方法并且同样安全。让这个答案成为硬币的另一面。
Django Web 框架使用这种方法。它是最常用的 Web 框架之一,因此我们可以放心,这种方法非常安全。
Django 使用以下参数生成令牌:
- 用户标识。
- 当前时间戳。这对于了解令牌的年龄很有用。
- 当前密码的哈希值。如果用户生成许多重置令牌并使用一个令牌重置其密码,则数据库中的哈希值将更改,但所有令牌仍具有旧哈希值,因此所有令牌将自动失效。
- 上次登录的时间戳。如果用户生成了重置令牌,但后来登录了他们的帐户,则此时间戳将在数据库中更新,但令牌仍具有旧时间戳,因此令牌将自动失效。
与 JWT 不同,最终令牌在纯文本中没有任何这些参数。仅发送令牌的十六进制摘要。
那么,如果令牌没有发送数据,你怎么知道这个令牌属于哪个用户呢?
有两种方法可以将令牌与用户相关联。
第一:可以用token发送用户的id,如:<user_id>:<the_token_value>
.
现在,当用户单击链接时,您可以从令牌中读取用户 id,使用之前的参数重新计算哈希,并将此哈希[参见下面的注释]与颁发的令牌进行比较。
如果它们匹配,则您允许用户重置密码。
第二: Django 处理这个问题的方式是它不发送带有令牌的用户 id,而是在重置 url 中。所以 Django 生成的重置 url 看起来像这样/reset-password/<user_id>/<token>
:
当用户点击链接时,您可以从 url 中读取用户 id,然后使用之前的参数重新计算 hash,并将这个 hash [参见下面的注释]与颁发的令牌进行比较。
重要的提示:
请不要像比较字符串那样比较哈希(即使用相等运算符)。这样比较容易受到定时攻击。
在 Python 中,有一个secrets.compare_digest
函数可以解决这个问题。请找到您的语言中的等价物来比较哈希值。