ch09
This commit is contained in:
parent
48cef734e0
commit
8704175145
54
ch09/hlib.py
Normal file
54
ch09/hlib.py
Normal file
@ -0,0 +1,54 @@
|
||||
# hlib.py
|
||||
# NOT A PYTHON MODULE - DO NOT ATTEMPT TO RUN
|
||||
|
||||
# hlib.py
|
||||
>>> import hashlib
|
||||
>>> hashlib.algorithms_available
|
||||
{'mdc2', 'sha224', 'whirlpool', 'sha1', 'sha3_512', 'sha512_256',
|
||||
'sha256', 'md4', 'sha384', 'blake2s', 'sha3_224', 'sha3_384',
|
||||
'shake_256', 'blake2b', 'ripemd160', 'sha512', 'md5-sha1',
|
||||
'shake_128', 'sha3_256', 'sha512_224', 'md5', 'sm3'}
|
||||
>>> hashlib.algorithms_guaranteed
|
||||
{'blake2s', 'md5', 'sha224', 'sha3_512', 'shake_256', 'sha3_256',
|
||||
'shake_128', 'sha256', 'sha1', 'sha512', 'blake2b', 'sha3_384',
|
||||
'sha384', 'sha3_224'}
|
||||
|
||||
>>> h = hashlib.blake2b()
|
||||
>>> h.update(b'Hash me')
|
||||
>>> h.update(b' now!')
|
||||
>>> h.hexdigest()
|
||||
'56441b566db9aafcf8cdad3a4729fa4b2bfaab0ada36155ece29f52ff70e1e9d'
|
||||
'7f54cacfe44bc97c7e904cf79944357d023877929430bc58eb2dae168e73cedf'
|
||||
>>> h.digest()
|
||||
b'VD\x1bVm\xb9\xaa\xfc\xf8\xcd\xad:G)\xfaK+\xfa\xab\n\xda6\x15^'
|
||||
b'\xce)\xf5/\xf7\x0e\x1e\x9d\x7fT\xca\xcf\xe4K\xc9|~\x90L\xf7'
|
||||
b'\x99D5}\x028w\x92\x940\xbcX\xeb-\xae\x16\x8es\xce\xdf'
|
||||
>>> h.block_size
|
||||
128
|
||||
>>> h.digest_size
|
||||
64
|
||||
>>> h.name
|
||||
'blake2b'
|
||||
|
||||
>>> hashlib.sha256(b'Hash me now!').hexdigest()
|
||||
'10d561fa94a89a25ea0c7aa47708bdb353bbb062a17820292cd905a3a60d6783'
|
||||
|
||||
|
||||
>>> import hashlib
|
||||
>>> h1 = hashlib.blake2b(b'Important data', digest_size=16,
|
||||
... person=b'part-1')
|
||||
>>> h2 = hashlib.blake2b(b'Important data', digest_size=16,
|
||||
... person=b'part-2')
|
||||
>>> h3 = hashlib.blake2b(b'Important data', digest_size=16)
|
||||
>>> h1.hexdigest()
|
||||
'c06b9af95d5aa6307e7e3fd025a15646'
|
||||
>>> h2.hexdigest()
|
||||
'9cb03be8f3114d0f06bddaedce2079c4'
|
||||
>>> h3.hexdigest()
|
||||
'7d35308ca3b042b5184728d2b1283d0d'
|
||||
|
||||
>>> import os
|
||||
>>> dk = hashlib.pbkdf2_hmac('sha256', b'Password123',
|
||||
... salt=os.urandom(16), iterations=100000)
|
||||
>>> dk.hex()
|
||||
'f8715c37906df067466ce84973e6e52a955be025a59c9100d9183c4cbec27a9e'
|
||||
15
ch09/hmc.py
Normal file
15
ch09/hmc.py
Normal file
@ -0,0 +1,15 @@
|
||||
# hmc.py
|
||||
import hmac
|
||||
import hashlib
|
||||
|
||||
|
||||
def calc_digest(key, message):
|
||||
key = bytes(key, 'utf-8')
|
||||
message = bytes(message, 'utf-8')
|
||||
|
||||
dig = hmac.new(key, message, hashlib.sha256)
|
||||
return dig.hexdigest()
|
||||
|
||||
|
||||
mac = calc_digest('secret-key', 'Important Message')
|
||||
print(mac)
|
||||
55
ch09/jwt/claims_auth.py
Normal file
55
ch09/jwt/claims_auth.py
Normal file
@ -0,0 +1,55 @@
|
||||
# jwt/claims_auth.py
|
||||
import jwt
|
||||
|
||||
|
||||
data = {'payload': 'data', 'iss': 'hein', 'aud': 'learn-python'}
|
||||
|
||||
|
||||
secret = 'secret-key'
|
||||
token = jwt.encode(data, secret)
|
||||
|
||||
|
||||
def decode(token, secret, issuer=None, audience=None):
|
||||
try:
|
||||
print(jwt.decode(token, secret, issuer=issuer,
|
||||
audience=audience, algorithms=["HS256"]))
|
||||
except (
|
||||
jwt.InvalidIssuerError, jwt.InvalidAudienceError
|
||||
) as err:
|
||||
print(err)
|
||||
print(type(err))
|
||||
|
||||
|
||||
decode(token, secret)
|
||||
|
||||
# not providing the issuer won't break
|
||||
decode(token, secret, audience='learn-python')
|
||||
|
||||
# not providing the audience will break
|
||||
decode(token, secret, issuer='hein')
|
||||
|
||||
# both will break
|
||||
decode(token, secret, issuer='wrong', audience='learn-python')
|
||||
decode(token, secret, issuer='hein', audience='wrong')
|
||||
|
||||
decode(token, secret, issuer='hein', audience='learn-python')
|
||||
|
||||
|
||||
"""
|
||||
$ python jwt/claims_time.py
|
||||
Invalid audience
|
||||
<class 'jwt.exceptions.InvalidAudienceError'>
|
||||
|
||||
{'payload': 'data', 'iss': 'hein', 'aud': 'learn-python'}
|
||||
|
||||
Invalid audience
|
||||
<class 'jwt.exceptions.InvalidAudienceError'>
|
||||
|
||||
Invalid issuer
|
||||
<class 'jwt.exceptions.InvalidIssuerError'>
|
||||
|
||||
Invalid audience
|
||||
<class 'jwt.exceptions.InvalidAudienceError'>
|
||||
|
||||
{'payload': 'data', 'iss': 'hein', 'aud': 'learn-python'}
|
||||
"""
|
||||
49
ch09/jwt/claims_time.py
Normal file
49
ch09/jwt/claims_time.py
Normal file
@ -0,0 +1,49 @@
|
||||
# jwt/claims_time.py
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from time import sleep, time
|
||||
|
||||
import jwt
|
||||
|
||||
|
||||
iat = datetime.now(tz=timezone.utc)
|
||||
nfb = iat + timedelta(seconds=1)
|
||||
exp = iat + timedelta(seconds=3)
|
||||
|
||||
|
||||
data = {'payload': 'data', 'nbf': nfb, 'exp': exp, 'iat': iat}
|
||||
|
||||
|
||||
def decode(token, secret):
|
||||
print(time())
|
||||
try:
|
||||
print(jwt.decode(token, secret, algorithms=['HS256']))
|
||||
except (
|
||||
jwt.ImmatureSignatureError, jwt.ExpiredSignatureError
|
||||
) as err:
|
||||
print(err)
|
||||
print(type(err))
|
||||
|
||||
|
||||
secret = 'secret-key'
|
||||
token = jwt.encode(data, secret)
|
||||
|
||||
|
||||
decode(token, secret)
|
||||
sleep(2)
|
||||
decode(token, secret)
|
||||
sleep(2)
|
||||
decode(token, secret)
|
||||
|
||||
|
||||
"""
|
||||
$ python jwt/claims_time.py
|
||||
1631043839.6459477
|
||||
The token is not yet valid (nbf)
|
||||
<class 'jwt.exceptions.ImmatureSignatureError'>
|
||||
1631043841.6480813
|
||||
{'payload': 'data', 'nbf': 1631043840, 'exp': 1631043842, 'iat':
|
||||
1631043839}
|
||||
1631043843.6498601
|
||||
Signature has expired
|
||||
<class 'jwt.exceptions.ExpiredSignatureError'>
|
||||
"""
|
||||
27
ch09/jwt/rsa/key
Normal file
27
ch09/jwt/rsa/key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAz87T1pNQnbPZLSxuHP8y0HQDcNsKpwd1LwtghPld9l0lK6f2
|
||||
pDNlJYwqI0il8JUfIfWE9VaO9KsxTfBtYPyLO8Tz7Nk+04KDlkQOKgpKJHrYTd+5
|
||||
iSre3D68IVHdxBku2VFh57uqBNJCqGBF0OgVHh/ms9f0kDVaaUNSj7kMu5sLVPWR
|
||||
vnZgyJN0v/LHS2y22tkXncj7wf9kC13qN36MsZ2qH4VF0f8/WTkE6zUSdTwY2ZEv
|
||||
K66zjmTfuFoQ8df5N5Pj4STQGk+2l6uU/kxNbBNWjYmJNp1EpdJJ3m9A1nDrRRQf
|
||||
M1HdckOCJVn+eYDrzD2W/bSMm/3SgqzM3o1ONQIDAQABAoIBADv9QgwUlNYhwBUZ
|
||||
WWRHi3y/sOqYYRfcwEhnaWKZtjVTqFJU949pQs63CscKxtSPx9/+x3Ynmbcp4F7W
|
||||
hEzWOr9zWNNL16YpbCEp3kjmlYa4a873QdFGLfW4qoiX4IQmnO6hXrkN5MPlitae
|
||||
jCkFhmse3HOYaQJkVIhEpgXVbsaNMvSa6woAfT5tVPSxDSEdB4a61zwSpssYfLMF
|
||||
ZFsHEV6mE6hkmfZxw22/I2I7Up0sV6f7KOgjoPaeqL6i5ibEhIWzDyQiKyW6YN3j
|
||||
moZNrIAHoq8oAP9wWavrpbnslp5c3SculUueVxdUv46LDy9RbmFOajdaJALFdY/D
|
||||
NI+X7WECgYEA+qBnI0cHmYfOqGeSVUsTPUk0uG8N2fDWh1m4GPRzU4a00Mh7nG+l
|
||||
8j54FDLqQeoWyWN0808dRMakjeHNzjIEi8paHbrrWIS+4Xp8pVH56iCG9wTtbj6j
|
||||
ni5CeoKFtRd3NcqwTOWznR+OvE02WzqE8kDQbNmT1Y68MPf/4vEpABkCgYEA1ENo
|
||||
pKknQv+e9ZapWtHk6DDtq/Xw/R+VadwfGBDdIU1XQsUjlTw/igeElrDQOPVao8xL
|
||||
SK9vCNvPUi+W0W241ZoMGCIeywGsnixKoRx7DWNxYchcPDWQtoZtrNUwkJBW/Lha
|
||||
iFMQDdQU7aLYJlwi09KeJB9w7JHNpi4R5N6Kkn0CgYB0jdApOckw/1V+P9xvyiBC
|
||||
ah8Yfp0Ec9pwy6qwAE750zWXuwSxdcLI64BQdHU4/jJmqdgjpvFHoNG6If7iG6S9
|
||||
lfdeyCTW37UdiycYgw0kcsgRbLs2f+77iyvjaXvhAe6wl3hx6okjUx/ANnBG8OoO
|
||||
91F2raDwuhaO92aLFfgpUQKBgAh1t1H4u+vbrEY1fwJzox/t80sLPlOYUqgpccPP
|
||||
yEBviK7MlKJ6CD5EFnC7E1Fx1e75UXbQJyi/OgAYjXZDXT6GKT08/uUwZ+TV9xN8
|
||||
wxrBf99Z0PNFX0MnEG/2/zyDxDPGVTVhuh4S+dKOzvaYbXrrxgnChwmmtf+NjoEE
|
||||
rZkxAoGAQvv6ZrKrhUMYIsgPVyjXe5E421cv/pj4KCsqdeclV+3vR87SwOfpej9y
|
||||
/gpZuEIHxzzgAz5eFgrGJjm/4VGqNEJ3PGxa6i6a0ld3Cv2j/qyjV8BvL+MtFH1r
|
||||
X4lEs3tRFn9LSqyeQjfDRP+Fv91OgKaRb6wJ8HWBnx8Jpkd19kU=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
1
ch09/jwt/rsa/key.pub
Normal file
1
ch09/jwt/rsa/key.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDPztPWk1Cds9ktLG4c/zLQdANw2wqnB3UvC2CE+V32XSUrp/akM2UljCojSKXwlR8h9YT1Vo70qzFN8G1g/Is7xPPs2T7TgoOWRA4qCkokethN37mJKt7cPrwhUd3EGS7ZUWHnu6oE0kKoYEXQ6BUeH+az1/SQNVppQ1KPuQy7mwtU9ZG+dmDIk3S/8sdLbLba2RedyPvB/2QLXeo3foyxnaofhUXR/z9ZOQTrNRJ1PBjZkS8rrrOOZN+4WhDx1/k3k+PhJNAaT7aXq5T+TE1sE1aNiYk2nUSl0kneb0DWcOtFFB8zUd1yQ4IlWf55gOvMPZb9tIyb/dKCrMzejU41 fab@fabmp.local
|
||||
30
ch09/jwt/rsa/keypwd
Normal file
30
ch09/jwt/rsa/keypwd
Normal file
@ -0,0 +1,30 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: AES-128-CBC,6B1FF5E922949C8E4AA51B8F5B0F0CBE
|
||||
|
||||
E6B7lpXoI0SXOImuBllCOoFPonK6JObMVOp2qrEagcVP9loRXOecRi/qG+nF/BJO
|
||||
W/FnY4OmsCYAgFPB281plp8eVodNr6XxcPZaQx92G9RlA5FSCWICWFfhOlcDo1bR
|
||||
1OAkmZjzWve3C2XNbOVpMweO3ElFzKy2KPzuBHaDVfuZm6pMMxqn4C+MzL6cYtF/
|
||||
a3aJRwRJJYF9OLOSpfoFCCVC/NfOjhczPLgNqRHnpnnAypddwju8R6KPJ1SkZaru
|
||||
Jd1hwAYzcFY/G21SwCzUp9MOp9faeomWYYiv8BT/65LOY/e8evITh9XDpe83S3KQ
|
||||
oY+7NiVZdQzjaw8lA9KRWQTsXTLSXAFaHJLmfITr/QnC1HhEPBiux3MY2vjU1QMJ
|
||||
JtsrlTqBjBIKMJh4RoqvNrcNCCCYaXleNmSObcGR75s41s9yGAaNNKKGfvvPvW5j
|
||||
69mOEqOmoKdtJuClpkeabqPXVqQLyf3CKZmqZbctmdAytTfZFRSTFw4VZZXKj/nR
|
||||
7eLPqlg4Q8F057LWlTklsRDZvQOp9Ci5kCFK+5fOQ79CBeBx59i121+tYSpLWos/
|
||||
bYOKcvSGfDghJYhGgfIg/H2e409VGkum1wTfEjD88n+wsjDQb94a0kiH2Kk/xqoy
|
||||
Bv24SSIPMgLHuWTJtkUOGESRm1op4URtVBmPURaAAacnXbr6jXyBrl8leN5T394U
|
||||
hn9Y9iNTTGBDFZL7gqZpSWzW5kI8Ju66Tt1WExlvHlb3h/0doIwlo0+hR6Rs4C4L
|
||||
2YuOiiV6IBjDPF2ySg2zaUfGSVkxFL85Jrt8yQCLh3ebGeVkOptx653kVm0wMWcy
|
||||
a8vxTAI4m8szB272ZzMWKf312q83RjRBHPw7dMM6tq9jQL0Aea9/Ia9fLElA8rH/
|
||||
1i3g4zz3yhTqctkqiKIk7CeMQcYC3IL2ddUHftcijWfjvBChhjvWupegFC2DM29r
|
||||
H0H5lRyQHT8daaNqWYR9PEL+n8Au/FAgtz4U7lgYVdHstTq7Mch+vnWfc/4Y7pEl
|
||||
UWZBSmyMl2dpDTGCD79XFgXTuoqtkuML788ll5sz8C0Bh+jVfDwPsc8pimABag/s
|
||||
0N7l3cPHJX5Vv3lSrVtOdsT7Athhkz4klHijou+a1efDNX+aEbC08oy4M3OaV2Ri
|
||||
o1yHpvZo9Wsw3IMmxuPswWd42BIweUrLeJy1PuO8dONwi4SzT2RBXIV3H56It9Ad
|
||||
XQK7BYAtOa1sBJ9tYycP/f9Bfg36SpjxNAwBQkQB9V8nNEDwW4818zQs7aLs5NkM
|
||||
BMIETlGBSMX+OQTOTNPoABlx+c0Xn6NSm3GpHr5biZOhmShQaDDBdnPV8yk1XQ9j
|
||||
06tO2LvaevhOVfGt0GuPwUQbOwoJ8dAFFM7A7//2vd/QFEtJKXQK3N2cQTDmvTch
|
||||
dBt4vpqgiZfwomglHqVw/8NHSIIdTHyVYjCCNQvK7OfjricjBIAMGio+XlNiCbwP
|
||||
Jpo5fRtZ3bJNLiHIDFYjelWJhSN+B/h0DfSo3XF6roncXC2ljZ07TDi66ju0j80Q
|
||||
/n5pc2m1ckjjf46tkuSsW6kQN1YBdpzLyyAHeuy9rAR/Vo8inPld7zJa7B4oOuow
|
||||
-----END RSA PRIVATE KEY-----
|
||||
1
ch09/jwt/rsa/keypwd.pub
Normal file
1
ch09/jwt/rsa/keypwd.pub
Normal file
@ -0,0 +1 @@
|
||||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDAvimJusND8agzuXFhG+d4sPrn6AqC69H/icI77baF15/ztE9b/SioeBl2B+1HI/EnkqMpK8vLJOAN27cHUps9yMkjdU4KrRqK9pjHaeiwxZ+24LVt4QzvT6DcpREymB5y1GX34hIFjcXZDZgV8BkWwVW6qrocLtdl9c4vycPbqAYZ7o/hJzlbmhRClpB1FYamlZhuiH+5neWXuWtN7yV91Pi+DS7l0SbX37ElOComvZy9ug2UVqrK4+mPwCAtxnppHZVQsyVd2l2rKARqE3LRn2mbvlUtInDhfCU12GjRqItbbDgrcKoKeAJrExPUfWc9W3Iknh/2Os0FcbJSr6PX fab@fabmp.local
|
||||
21
ch09/jwt/tok.py
Normal file
21
ch09/jwt/tok.py
Normal file
@ -0,0 +1,21 @@
|
||||
# jwt/tok.py
|
||||
import jwt
|
||||
|
||||
|
||||
data = {'payload': 'data', 'id': 123456789}
|
||||
algs = ['HS256', 'HS512']
|
||||
|
||||
token = jwt.encode(data, 'secret-key')
|
||||
data_out = jwt.decode(token, 'secret-key', algorithms=algs)
|
||||
print(token)
|
||||
print(data_out)
|
||||
|
||||
|
||||
# decode without verifying the signature
|
||||
jwt.decode(token, options={'verify_signature': False})
|
||||
|
||||
|
||||
# let's use another algorithm
|
||||
token512 = jwt.encode(data, 'secret-key', algorithm='HS512')
|
||||
data_out = jwt.decode(token512, 'secret-key', algorithms=['HS512'])
|
||||
print(data_out)
|
||||
28
ch09/jwt/token_rsa.py
Normal file
28
ch09/jwt/token_rsa.py
Normal file
@ -0,0 +1,28 @@
|
||||
# jwt/token_rsa.py
|
||||
import jwt
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
|
||||
|
||||
data = {'payload': 'data'}
|
||||
|
||||
|
||||
def encode(data, priv_filename, algorithm='RS256'):
|
||||
|
||||
with open(priv_filename, 'rb') as key:
|
||||
private_key = key.read()
|
||||
|
||||
return jwt.encode(data, private_key, algorithm=algorithm)
|
||||
|
||||
|
||||
def decode(data, pub_filename, algorithm='RS256'):
|
||||
|
||||
with open(pub_filename, 'rb') as key:
|
||||
public_key = key.read()
|
||||
|
||||
return jwt.decode(data, public_key, algorithms=[algorithm])
|
||||
|
||||
|
||||
token = encode(data, 'jwt/rsa/key')
|
||||
data_out = decode(token, 'jwt/rsa/key.pub')
|
||||
print(data_out)
|
||||
2
ch09/requirements/requirements.in
Normal file
2
ch09/requirements/requirements.in
Normal file
@ -0,0 +1,2 @@
|
||||
pyjwt
|
||||
cryptography
|
||||
14
ch09/requirements/requirements.txt
Normal file
14
ch09/requirements/requirements.txt
Normal file
@ -0,0 +1,14 @@
|
||||
#
|
||||
# This file is autogenerated by pip-compile with python 3.9
|
||||
# To update, run:
|
||||
#
|
||||
# pip-compile requirements.in
|
||||
#
|
||||
cffi==1.14.5
|
||||
# via cryptography
|
||||
cryptography==3.4.7
|
||||
# via -r requirements.in
|
||||
pycparser==2.20
|
||||
# via cffi
|
||||
pyjwt==2.1.0
|
||||
# via -r requirements.in
|
||||
30
ch09/secrs/secr_gen.py
Normal file
30
ch09/secrs/secr_gen.py
Normal file
@ -0,0 +1,30 @@
|
||||
# secrs/secr_gen.py
|
||||
import secrets
|
||||
from string import digits, ascii_letters
|
||||
|
||||
|
||||
def generate_pwd(length=8):
|
||||
chars = digits + ascii_letters
|
||||
return ''.join(secrets.choice(chars) for c in range(length))
|
||||
|
||||
|
||||
def generate_secure_pwd(length=16, upper=3, digits=3):
|
||||
if length < upper + digits + 1:
|
||||
raise ValueError('Nice try!')
|
||||
|
||||
while True:
|
||||
pwd = generate_pwd(length)
|
||||
if (any(c.islower() for c in pwd)
|
||||
and sum(c.isupper() for c in pwd) >= upper
|
||||
and sum(c.isdigit() for c in pwd) >= digits):
|
||||
return pwd
|
||||
|
||||
|
||||
print(generate_secure_pwd())
|
||||
print(generate_secure_pwd(length=3, upper=1, digits=1))
|
||||
|
||||
"""
|
||||
$ python secr_gen.py
|
||||
nsL5voJnCi7Ote3F
|
||||
J5e
|
||||
"""
|
||||
31
ch09/secrs/secr_rand.py
Normal file
31
ch09/secrs/secr_rand.py
Normal file
@ -0,0 +1,31 @@
|
||||
# secrs/secr_rand.py
|
||||
import secrets
|
||||
|
||||
|
||||
# utils
|
||||
print(secrets.choice('Choose one of these words'.split()))
|
||||
|
||||
print(secrets.randbelow(10 ** 6))
|
||||
|
||||
print(secrets.randbits(32))
|
||||
|
||||
|
||||
# tokens
|
||||
print(secrets.token_bytes(16))
|
||||
|
||||
print(secrets.token_hex(32))
|
||||
|
||||
print(secrets.token_urlsafe(32))
|
||||
|
||||
# compare digests against timing attacks
|
||||
secrets.compare_digest('abc123', 'abc123')
|
||||
|
||||
"""
|
||||
$ python secr_rand.py
|
||||
one
|
||||
504156
|
||||
3172492450
|
||||
b'\xda\x863\xeb\xbb|\x8fk\x9b\xbd\x14Q\xd4\x8d\x15}'
|
||||
9f90fd042229570bf633e91e92505523811b45e1c3a72074e19bbeb2e5111bf7
|
||||
bl4qz_Av7QNvPEqZtKsLuTOUsNLFmXW3O03pn50leiY
|
||||
"""
|
||||
16
ch09/secrs/secr_reset.py
Normal file
16
ch09/secrs/secr_reset.py
Normal file
@ -0,0 +1,16 @@
|
||||
# secrs/secr_reset.py
|
||||
import secrets
|
||||
|
||||
|
||||
def get_reset_pwd_url(token_length=16):
|
||||
token = secrets.token_urlsafe(token_length)
|
||||
return f'https://example.com/reset-pwd/{token}'
|
||||
|
||||
|
||||
print(get_reset_pwd_url())
|
||||
|
||||
|
||||
"""
|
||||
$ python secr_reset.py
|
||||
https://example.com/reset-pwd/dfVPEPl_pCkQ8YNV4er-UQ
|
||||
"""
|
||||
Loading…
x
Reference in New Issue
Block a user