我是否正确生成了电子邮件链接令牌?

信息安全 账户安全 一次性密码 令牌
2021-08-27 00:33:39

我正在开发一个可靠的令牌生成和验证系统,主要用于确认电子邮件中的链接(重置​​密码请求、更改电子邮件流、激活帐户等)。

有几件事是强制性的:

  • 令牌在系统(数据库中)中必须是唯一的(即使同时生成两个)

  • 令牌必须是一次性使用

  • 令牌必须有过期
  • 令牌无法猜测

从那我决定生成这样的令牌:

token = sha256(user.id + time + uuid(v4) + secret)

该令牌不需要携带任何过期信息,因为它与外部的那些列一起保存在数据库中。

  1. 这个令牌是否符合我以上几点要求?如果没有,如何修改我的方法?
  2. 如果这个令牌符合我的要求,有没有办法在实现我的目标的同时简化它?

我问这个,因为我知道这些类型的一次性使用令牌发送到电子邮件有一些已知的漏洞,我不确定我是否安全。

3个回答

如果您无论如何都将所有相关信息(令牌、到期时间、用户)存储在数据库中,那么您唯一需要确定的关于令牌的事情就是无法猜测令牌。

如果这两个中的至少一个成立,则无法猜测您的令牌:

  • 秘密仍然是秘密。它必须具有非常高的熵,并且永远不会泄漏。
  • UUID 是使用安全随机源生成的。

实际上,您的系统比它需要的更复杂。由于令牌仅用于在数据库中查找信息,而不是对其进行验证,因此您可以只使用 UUID 而不使用其他任何东西 - 没有哈希、没有秘密、没有其他数据。您唯一需要确保的是 UUID 是使用安全的随机源生成的。

如果您将此信息仅用于验证目的并且已经将其存储在数据库中,则生成一个随机 UUID 并将其与您需要的元数据(时间戳、用户)一起存储在数据库中就足够了。我认为将这些列散列到令牌中没有额外的好处。此设置要求您通过数据库同步,确保一次性使用等。

您还可以做的是使用加密技术以类似于 Amazon S3 或 JWT 的方式对您的 URL 进行数字签名。

您可以将您的常规用户 ID 和到期时间戳放在 URL 中。使用需要保持真实的其他数据对此进行签名。密码重置将按此实施

/reset-password?userid=X&expires=Y&signature=Z

计算你的签名

    sign(userId=X,expires=Y,currentPasswordHash=..., privateKey)

当用户单击此链接时,使用签名验证完整性并检查它是否未过期。由于加密签名,用户不能在不使签名失效的情况下修改 URL 中的两个参数。将 currentHasswordHash 添加到签名中确保一旦用户成功更改密码,即使签名尚未过期,签名也不再有效。

您甚至可以利用不需要在数据库中存储任何内容的事实。注册/选择加入不需要保存最终用户的电子邮件地址,除非他们实际点击了选择加入链接。

它需要一些额外的步骤,但由于它是无状态的,因此可以更好地扩展。此外,您不必清理数据库并删除旧令牌。当然,您需要不惜一切代价保护您的私钥,否则其他人可能会生成有效的 URL。

我认为它是安全的。由于您使用的是用户 ID 时间、uuid 和一个秘密。

如果有人要创建自己的代币,那么单独的时间会使它成为一个困难因素,并且添加其他秘密会使它成为一件烦人的事情。因此,即使知道您的方法,我也必须遍历时间、uuid 和秘密。为了什么?令牌在系统中并且未过期的机会?如果您注意到一个客户端尝试加载和加载令牌,您只需阻止它们。

秘密和 uuid 是你的秘密,用户不知道这些。有了以上知识,你每次都会有一个唯一的令牌(由于添加的时间)。

但是你为什么要制定自己的方法?已经有很多代币生成解决方案。