как лучше всего создать токен сброса в python?

Я пытаюсь сделать процесс проверки для сброса пароля, я использовал два значения: время эпохи, и я хочу использовать старый пароль пользователя (pbkdf2) в качестве ключа,

Поскольку я не хочу получать символы, отличные от ASCII, я использовал библиотеку SimpleEncode, поскольку она быстро, так как это только BASE64 с используемым ключом, но проблема в том, что пароль слишком длинный (196 символов), поэтому я получаю длинный ключ!

Что я сделал, так это разделил результат code = simpleencode.encode(key,asci)[::30], но он не будет уникальным!

Чтобы понять, как это работает, я попробовал процесс сброса Facebook, но дано число! Итак, как работает этот процесс, разве они не используют ключ, чтобы кому-то было трудно подделать ссылку для сброса чьего-то пароля?

Обновление: как будет работать алгоритм:

1- получить время с помощью epoche time.time()

2- сгенерируйте Base64 времени эпохи (для использования для URL-адреса) и значения времени эпохи + ключ, этот ключ PBKDF2 (пароль).

3- сгенерируйте URL-адрес www.example.com/reset/user/Base64(time.time()) и отправьте этот URL + simpleencode.encode(key,asci)[::30]

4- когда пользователь нажимает на URL-адрес, он вводит сгенерированный код, этот сгенерированный код, если он совпадает с URL-адресом, то пусть он изменит пароль, иначе это забытый URL-адрес!


person Abdelouahab Pp    schedule 05.02.2013    source источник
comment
Он не обязательно должен быть уникальным, если вы подтверждаете, что его еще нет в базе данных, и он достаточно длинный, чтобы никто не догадался о нем.   -  person Wooble    schedule 05.02.2013
comment
что я сделал, так это поместил это значение в пользовательский документ (таблицу), чтобы оно было в базе данных.   -  person Abdelouahab Pp    schedule 05.02.2013
comment
Вы не должны не раскрывать (хэш) пароль пользователя в любой форме как часть токена сброса. Это позволило бы злоумышленнику узнать хэш, который он затем мог бы попытаться взломать.   -  person zwol    schedule 05.02.2013
comment
@Zack, хакер не может получить доступ к паролю, это только хэш, который используется для вычисления чего-то на стороне сервера, я обновил алгоритм   -  person Abdelouahab Pp    schedule 06.02.2013
comment
Вы раскрываете PBKDF2 (пароль). Это обратимо с помощью грубой силы, если пароль слабый, что, вероятно, будет, если на самом деле это пароль пользователя.   -  person zwol    schedule 06.02.2013
comment
как это выставляется? пользователь никогда не узнает, что именно его пароль был использован со значением времени эпохи для создания токена.   -  person Abdelouahab Pp    schedule 06.02.2013
comment
@AbdelouahabPp Это все еще дыра в безопасности, и дыра в безопасности вряд ли будет использована до тех пор, пока она не будет использована. Просто не вводите их, когда их можно избежать. Вы всегда можете безопасно хэшировать пароль с солью, отличной от той, которую вы используете для хранения паролей.   -  person millimoose    schedule 06.02.2013
comment
@AbdelouahabPp Кроме того, 30 символов случайных данных в формате base64 (т. Е. Хэш) определенно достаточно уникальны.   -  person millimoose    schedule 06.02.2013
comment
да, но пользователь не будет вводить все эти 30 символов, пользователи всегда ленивы :p   -  person Abdelouahab Pp    schedule 06.02.2013
comment
@AbdelouahabPp В этом случае вы можете включить идентификатор пользователя в хешированный ключ. Поскольку внутренний идентификатор пользователя всегда уникален, идентификатор пользователя и пароль будут уникальными, и, следовательно, весьма вероятно, что их 320-битный хеш будет уникальным. (Тем не менее, метод случайного GUID, при котором вы явно сохраняете запросы на сброс в базе данных, кажется проще.)   -  person millimoose    schedule 06.02.2013
comment
@AbdelouahabPp Кроме того, если вы можете создать PBKDF2 (пароль), это означает, что вы храните пароли в открытом виде в своей базе данных, что является плохой идеей. Не делай этого. Ваша база данных должна содержать хэши паролей, желательно со случайным значением, заданным пользователем. Бонусные баллы за то, что еще и засолили их случайным числом из небольшого диапазона, который вы даже нигде не храните. (Хотя это немного усложняет алгоритм входа в систему.)   -  person millimoose    schedule 06.02.2013
comment
нет, пароль хранится с помощью pbkdf2, и этот алгоритм использует соль, поэтому я просто попытался использовать сгенерированный текст, а не сам пароль, но теперь я думаю, что буду придерживаться случайно сгенерированных цифр ''.join(choice( цифры) для i в xrange(4)) , а что касается идентификатора пользователя, я использую их электронную почту в качестве первичного ключа, так что это немного сложно: p   -  person Abdelouahab Pp    schedule 06.02.2013


Ответы (3)


Не уверен, что это лучший способ, но я бы, вероятно, просто сгенерировал UUID4, который можно использовать в URL-адресе для сброса пароля и истечения срока его действия через «n» время.

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

Вы можете использовать что-то вроде http://redis.io для хранения этого ключа со значением соответствующего идентификатора пользователя и установить его время жизни. Итак, когда что-то приходит с http://example.com/password-reset/8c05904f0051419283d1024fc5ce1a59, действителен, и если да, то разрешает изменения для установки нового пароля.

Если вам нужен «пин-код проверки», сохраните вместе с токеном небольшой случайный ключ, например:

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

И просьба войти по ссылке сброса.

person Jon Clements♦    schedule 05.02.2013
comment
спасибо за этот совет, и он безопасен для URL, но как насчет метода, который я использовал, обновил вопрос - person Abdelouahab Pp; 06.02.2013
comment
@AbdelouahabPp Я лично считаю, что ваш метод чрезмерно усложняет проблему - и нет причин использовать какую-либо информацию о пользователе для создания токена ... - person Jon Clements♦; 06.02.2013
comment
поэтому, если я использую метод UUID, я должен сгенерировать код, который пользователь должен ввести для ввода? - person Abdelouahab Pp; 06.02.2013
comment
чтобы убедиться, что URL-адрес не был забыт - person Abdelouahab Pp; 06.02.2013
comment
@AbdelouahabPp Если вы имеете в виду подделку - крайне маловероятно (1 из 340 282 366 920 938 463 463 374 607 431 768 211 456 шансов) создать выпущенный токен (не говоря уже о том, что он все еще действителен). Добавил пример простого ПИН-кода, если так вам будет удобнее... - person Jon Clements♦; 06.02.2013
comment
hihihihihhi я принимаю ваше решение: p - person Abdelouahab Pp; 06.02.2013
comment
Я не рекомендую это. Вывод библиотеки random небезопасен и его очень легко предсказать. Используйте безопасные случайные числа, например модуль secrets (3.6) или os.urandom (‹3.6) - person Azsgy; 07.06.2017
comment
@Atsch, какое отношение это имеет к UUID, что является ключевым моментом в этом ответе? - person Jon Clements♦; 07.06.2017
comment
uuid подходит, так как использует os.urandom, но решение с булавкой потенциально опасно - person Azsgy; 07.06.2017
comment
@Atsch хорошо - я говорю, что PIN-код следует хранить вместе с токеном, поэтому они используются в комбинации. Это довольно бессмысленно, но ввод чего-либо иногда заставляет людей чувствовать, что происходит что-то вроде безопасности. - person Jon Clements♦; 07.06.2017
comment
приношу свои извинения, я просматривал его раньше и был встревожен, увидев, что кто-то использует random для токенов, но оказалось, что это не очень точно. Однако может быть удобно отказаться от этого, прежде чем кто-то решит использовать его без токена. - person Azsgy; 07.06.2017
comment
@Atsch здесь уже поздно, и я на мобильном телефоне, поэтому я добавлю немного, когда смогу, чтобы уточнить. Спасибо, что указали на это. - person Jon Clements♦; 07.06.2017

Самый простой способ — использовать библиотеку ItsDangerous:

Вы можете сериализовать и подписать идентификатор пользователя для отказа от подписки на информационные бюллетени в 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
person Doobeh    schedule 05.02.2013
comment
может ли кто-нибудь сказать мне, как работает timestampsigner, я разрабатываю веб-приложение, для этого мне нужно генерировать ссылки для забвения пароля на основе времени, и иногда срок действия ссылок должен истекать - person Mandava Geethabhargava; 20.11.2019
comment
Если вам нужно увидеть, как это работает на самом деле, вы можете посмотреть исходный код библиотеки github.com/pallets/itsdangerous/blob/master/src/itsdangerous/ - person Doobeh; 21.11.2019

Почему бы просто не использовать 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()
person lordvcs    schedule 05.02.2021
comment
Спасибо за этот дополнительный ответ. Есть ли недостатки, если идентификатор пользователя (внутри jwt) предоставляется клиентом (шаг 2, проверка и сохранение нового пароля)? - person Freude; 28.06.2021
comment
это должно быть хорошо, так как ваш внутренний код отправит электронное письмо со ссылкой для сброса, даже если злоумышленник введет чужой идентификатор, только правильный пользователь будет иметь доступ к своему почтовому ящику, и только он сможет нажать на сброс ссылка на сайт - person lordvcs; 28.06.2021