15

我正在尝试为密码重置进行验证过程,我使用的是两个值:纪元时间,我想使用用户的旧密码(pbkdf2)作为密钥,

因为我不想获取非 ASCII 字符,所以我使用了 SimpleEncode 库,因为它速度很快,因为它只是一个带有密钥的 BASE64,但问题是密码太长(196 个字符)所以我得到一个长密钥!

我所做的是拆分结果code = simpleencode.encode(key,asci)[::30],但这不会是唯一的!

为了了解它是如何工作的,我尝试了 Facebook 重置过程,但给出的是一个数字!那么这个过程是如何工作的,他们不是使用密钥来使某人难以伪造链接来重置某人的密码吗?

更新:算法将如何工作:

1- 使用 epoch 获取时间time.time()

2-生成纪元时间的Base64(用于URL)和纪元时间值+一个密钥,这个密钥是PBKDF2(密码)。

3-生成网址 www.example.com/reset/user/Base64(time.time()) 并发送此网址 +simpleencode.encode(key,asci)[::30]

4-当用户点击网址时,他把生成的代码,这个生成的代码,如果与网址匹配,则让他修改密码,否则,这是一个忘记网址!

4

3 回答 3

33

不确定这是最好的方法,但我可能只是生成一个 UUID4,它可以在 URL 中用于重置密码并在“n”时间后过期。

>>> import uuid
>>> uuid.uuid4().hex
'8c05904f0051419283d1024fc5ce1a59'

您可以使用http://redis.io之类的东西来保存该密钥,并使用适当的用户 ID 值并设置其生存时间。因此,当某些内容来自http://example.com/password-reset/8c05904f0051419283d1024fc5ce1a59时,它会查看它是否有效,如果有效,则允许更改以设置新密码。

如果您确实想要一个“验证销”,那么将一个小的随机密钥与令牌一起存储,例如:

>>> from string import digits
>>> from random import choice
>>> ''.join(choice(digits) for i in xrange(4))
'2545'

并要求在重置链接上输入。

于 2013-02-05T18:07:01.153 回答
21

到目前为止最简单的方法是使用ItsDangerous库:

您可以对用户 ID 进行序列化和签名,以将新闻通讯退订到 URL。这样您就不需要生成一次性令牌并将它们存储在数据库中。任何类型的帐户激活链接和类似的东西都是一样的。

您还可以嵌入时间戳,因此可以非常轻松地设置时间段,而无需涉及数据库或队列。它全部经过加密签名,因此您可以轻松查看它是否被篡改。

>>> from itsdangerous import TimestampSigner
>>> s = TimestampSigner('secret-key')
>>> string = s.sign('foo')
>>> s.unsign(string, max_age=5)
Traceback (most recent call last):
  ...
itsdangerous.SignatureExpired: Signature age 15 > 5 seconds
于 2013-02-05T22:10:59.740 回答
2

为什么不直接使用 jwt 作为令牌,它也可以为它设置一个过期时间,所以它也可以为令牌设置一个过期日期。

  1. 生成使用密钥加密的令牌(JWT)
  2. 发送包含带有令牌的链接作为查询参数的邮件(当用户打开链接时,页面可以读取令牌)
  3. 在保存新密码之前验证令牌

为了生成 jwt 令牌,我使用pyjwt. 下面的代码片段显示了如何在 24 小时(1 天)的到期时间下完成并使用密钥签名:

import jwt
from datetime import datetime, timedelta, timezone

secret = "jwt_secret"
payload = {"exp": datetime.now(timezone.utc) + timedelta(days=1), "id": user_id}
token = jwt.encode(payload, secret, algorithm="HS256")
reset_token = token.decode("utf-8")

下面的代码片段显示了如何在 django 中设置令牌验证和新密码。如果令牌已过期或被篡改,则会引发异常。

secret = "jwt_secret"
claims = jwt.decode(token, secret, options={"require_exp": True})
# Check if the user exists
user = User.objects.get(id=claims.get("id"))
user.set_password(password)
user.save()
于 2021-02-05T12:50:38.703 回答