質問

I'm trying to make a validation process for a password reset, what i've used are two values: the epoch time, and i want to use the users's old password (pbkdf2) as a key,

Since i dont want to get non ASCII characters, i've used SimpleEncode library because it's fast since it's only a BASE64 with a key used, but the problem is that the password is too long (196 chars) so i get a long key!

What i've done is split the result code = simpleencode.encode(key,asci)[::30], but this will not be unique!

To get an idea how it works, i've tried Facebook reset process, but what is given is a number! so how this process works, don't they use a key to make it hard for someone to forge a link to reset someone's password?

Update: how the algorithme will work:

1- get the time using epoche time.time()

2- generate the Base64 of the epoche time (to use for the URL) and the epoch time value + a key, this key is PBKDF2(password).

3- generate the url www.example.com/reset/user/Base64(time.time()) and send this URL + the simpleencode.encode(key,asci)[::30]

4- when the user clicks on the URL, he put the generated code, this generated code, if it matches with the URL, then let him modifiy the password, else, it is a forget URL!

役に立ちましたか?

解決

Not sure it's the best way, but I'd probably just generate a UUID4, which can be used in a URL to reset the password and expire it after 'n' amount of time.

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

You could use something like http://redis.io to hold that key, with a value of the appropriate user ID and set its time to live. So, when something comes in from http://example.com/password-reset/8c05904f0051419283d1024fc5ce1a59 it looks to see if it's valid and if so then allows changes to set a new password.

If you did want a "validation pin", then store along with the token, a small random key, eg:

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

And request that be entered on the reset link.

他のヒント

Easiest way by far is to use the ItsDangerous library:

You can serialize and sign a user ID for unsubscribing of newsletters into URLs. This way you don’t need to generate one-time tokens and store them in the database. Same thing with any kind of activation link for accounts and similar things.

You can also embed a timestamp, so very easily to set time periods without having to involve databases or queues. It's all cryptographically signed, so you can easily see if it's been tampered with.

>>> 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

Why not just use a jwt as token for this purpose, its also possible to set an expiration time to it, so its also possible to put an expiration date to the token.

  1. Generate token(JWT) encrypted with a secret key
  2. Send mail containg a link with the token as a query paramater(When the user opens the link the page can read the token)
  3. Verify the token before saving the new password

For generating jwt tokens I use pyjwt. The below code snippet shows how it can be done with an expiry time of 24 hours(1 day) and signed with a secret key:

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")

Below snippet shows how the verification of token and the new password can be set in django. If the token has expired or has been tampered with, it will raise an exception.

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()
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top