How to delete the URLSafeTimedSerializer token after use in python? - python

I have created a token using python itsdangerous URLSafeTimedSerializer for forgot password functionality. The token is getting expired after the max-age but when the password is reset then i need to expire that token, so that the user is not able to reset password with the token again and again till it is getting expired. So, the question here is how to expire the token after the user resets password?

Despite the comment of #Uber , I understand the question. Also I believe this is more a design question, so posting code would not make that much sense. This time I'll provide an answer, but remember to properly write the question for the future times, e.g. providing a scenario, like your database table and better describing the sequence of activities for resetting the password (see https://stackoverflow.com/help/how-to-ask).
So, say you generate a token via the URLSafeTimedSerializer library as follows:
from itsdanger import URLSafeTimedSerializer
ts = URLSafeTimedSerializer("my-secret-key")
token = ts.dumps(email, salt="my-salt")
In order to expire such token, you have different possibilities:
Create a blacklist of already used tokens (not a good idea as it would become too long and you may have clashes when generating tokens).
Add a 'token_reset' field to your user table, and store the current valid token there. As soon as the user uses it, delete it from the table. If no token exists for an user, it'll mean that he/she has already used it.
Add a timestamp to your token, so that when you get it back (you can decrypt it with the itsdanger library), you know if it has expired or not (though a user may use it twice in 5 minutes).
There may be other ways to solve such problem, but solution #2 is the one used most frequently (from my experience). If other users have other suggestions, feel free to edit this answer.

Related

Serialisation and timed web tokens in python flask

Hi I am struggling to understand the following example.
I want to add functionality for a user to reset password for my website.
As a lot of sites do I want to send a token to the users email that will let them reset their password.
I am following a guide that suggests using a python module called itsdangerous.
I have been given the following code as a simple example from the tutorial to understand how the module works before deploying to my website:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
s = Serializer('secret_key',30)
token = s.dumps({'usr_id': 1}).decode('utf-8')
s.loads(token)
Now if I run this here is what happens:
I use s to create a token that allows me to take a dictionary {'usr_id':1} then if I run s.loads(token) within 30 seconds I can get this dictionary {'usr_id':1} back otherwise I get an error.
Can anyone explain (in a simple way for a beginner) what is going on here?
I don't really understand what is happening and I don't see what the secret_key argument to the Serializer is doing?
Also if someone could explain how this kind of code can help me with allowing users to get an email to reset their password that would be great. Thanks!
So this kind of serialization provided by itsdangerous library is JSON Web Token based. In order to create a JSON Web Token you need a secret key, which is a signature to certify that the information tokenized was provided by you and can only be edited with this signature.
A JSON Web Token can be read by anyone but can only be edited by someone who knows the secret key - so you shouldn't tokenize sensitive information like a password, but a user id is ok, which alongside a expiry time set is just enough to check its identity. You must hide your secret key and keep it safe.
A good usage in your case would be to send a expiring token - let's say one day - to the user email, to proof it has authorization to change the password. And after one day it'll be invalid, so you won't compromise your system.
What is the JSON Web Token structure?
In its compact form, JSON Web Tokens consist of three parts separated by dots (.), which are:
Header: The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.
Payload: The information and the expiry time is held here.
Signature: The signature is used to verify the message wasn't changed along the way, and, in the case of tokens signed with a secret key, it can also verify that the sender of the JWT is who it says it is.
Therefore, a JWT typically looks like the following. xxxxx.yyyyy.zzzzz
You can play around seeing what is inside a JWT by pasting it on the link below:
https://jwt.io/
More info on:
https://jwt.io/introduction/

How can I connect myself as an other user?

Hello I am a developper of a django project and I have to check that my dev is okay.
To do this, I have to connect myself as a user but I have just his email not his password. I found in the table User this the email and the password but the password begins by this : pbkdf2_sha256 ...
So I guess the password is encrypted. Is there a way easier to do this ?
Thank you for your help !
Depending on how the passwords are hashed while new users are created, you could just replace the user's hash with another one :
Go in database and copy the passwordHash of the user you would like to log with
Store this hash somewhere (you will need it to revert the change)
Copy the hash of another user (a user for whom you know the password)
Paste the hash of this user in place of the hash of the user you want to log in
If the hashs are generated the same way, you will be able to log with the other user's password.
Then when you are done, revert the changes in database.
Hope it helps.
Passwords in django are one way encrypted so it cannot be decrypted hence there is no way of finding the actual password of any user from the user table in database.
If you want to login on another user's behalf without having to know their password then you can write some complicated code to achieve that, or use a third party package.
django-hijack looks like a good one. Its docs has good explaination on how to use it so I am not going to go through that here. If you don't like this package you can see the list of packages for this purpose here on djangopackages and choose the one you like.
django-hijack usage:
Complete the Installation and After Installation steps first.
Make a post request to /hijack/<user_id> where <user_id> is the column id of table user. If you have not updated the field id on model user it will be 1, 2, 3, .... So the url will be similar to /hijack/1/
Make a post request to /hijack/username/<username> if you want to hijack by username. eg. /hijack/username/awesome_username/
Make a post request to /hijack/email/<user email>/ if you want to hijack by email of user. eg. /hijack/email/awesome#email.com/
After making a post request to one of these urls as superuser you will be redirected to the specified HIJACK_LOGIN_REDIRECT_URL in settings.py
I am not sure if django-hijack will work on python 3 or newer versions of Django. If it doesn't works try out django-impersonate which provides the same functionality in a similar way and officially supports python 3.6+ and django 1.11+

check user security for modifying objects

I am thinking about security implementation for my python web app.
For example I have user and profiles.
Each user can edit his profile by sending POST at /profile/user_id
On every request i can get session user_id and compare it with profile.user_id if they not same - raise SecurityException().
But I also can do it another more common way:
generate for each profile
secret = hash(profile_data+secret_key)
and render for auth. users links like this:
/profile/4?key=secret
So idea is to generate secret key based on editable object data and check this key on server side. If user don't know secret key it can't get links to edit other profile, and so can't modify them.
How this method of protection called?
Has it any problems in comparsion with session based user_id check?
/profile/4?key=secret
Practical issues:
it's generally a bad idea to put a secret in a URL. URLs leak easily through logs, history, referrers etc. Also it breaks navigation. It's better to put the secret in a cookie or POST data.
you would typically include a time limit in the signed data, so tokens aren't valid forever
you would typically include some kind of flag/counter/state on the user information and in the signed data, so that a user can update something (eg change password) to invalidate previously-issued tokens
you also have to ensure that the lifecycle of signed data can't be longer than that of the token, eg that you can't delete user 4 and create a new user 4 for whom the token is still valid
the hash you would use would be an HMAC using a server-side secret for the key
Signed authentication tokens are typically used as an alternative to database-backed session storage, for performance or operational reasons. It's certainly possible to do sessions securely with signed tokens, but if you're already using stored sessions for other purposes anyway you don't have a lot to gain.

How to implement "Incorrect username/password" hint for a webservice using Flask HTTP Auth?

I have a client app that interacts with a web service to retrieve account information. There's a requirement that the user is notified if they mistyped the username/password. I'm modifying the web service to return something to my client to provide a hint to the user that there's an error in input.
How do I correctly implement the "username/password" not found for a web service using Python?
Do I tell the user that the username exists, but the password is incorrect?
Do I tell the user that there is no such username, but the password matched something?
Do I show a generic username/password combination is not found?
Do I use different status codes for different situations or provide a JSON payload with the error?
here's my code so far:
from flask.ext.httpauth import HTTPBasicAuth
accounts = [
["user0", "password0"],
["user1", "password1"],
]
#app.route('/accountlist')
#auth.login_required
def accountlist()
username = auth.username();
if ... : #check if accounts does not have the given username
#notify the sender that there is no such username
return Response('Not Authorized', 401, {'WWW-Authenticate': 'Basic'})
else:
#proceed to check password and retrieve/return account information
Do I show a generic username/password combination is not found?
Yes. Why do you think this is "generic"? Because it is the standard. This is the correct way because than a hacker can't go phishing for usernames.
Do I tell the user that the username exists, but the password is incorrect?
No, letting the user know that the username is correct is a user enumeration vulnerability. You are letting an attacker know which usernames are valid allowing them to narrow their target range. This would be useful if they later decided to try a brute force attack as they already know the usernames are correct and now they only need a working password.
Do I tell the user that there is no such username, but the password matched something?
Definitely not. This would mean that the attacker now had a valid password and could use any other username enumeration vulnerability on your site in order to try and find a valid username. Another common username enumeration location is the forgotten password form - many sites report back that there is no such username allowing an attacker to refine their list. Alternatively, they could use this password and brute force a username from it which may be a much easier job as usernames shouldn't benefit from being complex.
As an aside to this, you should be storing your passwords salted and hashed using a secure, slow algorithm such as bcrypt. This should mean it is not possible for you to practically check to see if any password matches the one entered.
Do I show a generic username/password combination is not found?
Yes!
Do I use different status codes for different situations or provide a JSON payload with the error?
Your JSON could return true or false to let the calling JavaScript know whether authentication was successful. If you ever develop any brute force protection, this should be accomplished by introducing a delay in the response rather than hard locking accounts. Hard locking accounts leads to a DoS attack as an attacker can lock out a valid account by repeatedly using the wrong password on purpose. For this reason, only a true/false response is really needed to let the user know if they were successful. Even if the account was hard locked, I would return false but include in the message that the user should contact technical support if they believe they should have access with the password provided.
You don't mention what kind of data you're serving but if you're working in financial or health care data: make it so either the user can log in or they cannot, you shouldn't endeavor to give them any information as to why.
If you want you can tell them that the username is incorrect but you cannot suggest other usernames. And, of course, you cannot give any information about what might be wrong with the password, just tell them that it's incorrect.
About the code you presented, I realize you didn't really ask for coding advice, still, I do tons of code reviews and consistently see the same issues over-and-over with these roll-your-own authentication schemes. If your code is ever audited the auditor will likely find the following issues:
You must never hardcode passwords.
You must never persist a password in cleartext, always use an irreversible hash (SHA-1 or greater) when a password is received and only work with the hash value
Your applicatoon should 'fail-closed', meaning set up the accountList() function to return a 'not authorized' prior to the if statement and prior to calling any functions that would throw an exception (like a database access). Make the auth check in the if statemnt. That way if something fails in the things that the if statement calls (say an exception in data access or file i/o) the user fails to log in.

Is there anything wrong with this password reset procedure?

Is there anything wrong with this procedure?
Enter in username and email in a reset form.
Flask creates a really long random string and stores it into the session under "reset_code". Session will also have a key for "reset_pw_username"
Flask sends an email with a link to the path /password_reset/reset_pw_username/reset_code
That link displays a form where the customer can reset the password if the session reset code matches the session reset_code item. Otherwise it bombs out.
The session will expire the reset code after an hour.
If I can enter a username and email address, then I can get a reset token for any user of your service emailed to me. Maybe you should check that the email address is one that actually belongs to the user whose password is going to be reset.
You must ensure that username and email entered match one of the accounts (or use emails as username in the first place).
From a usability perspective, this won't work if the browser that displays the link contained in the email is not the same as the one initially used.
Apart from that, you should pay special attention to the randomness (not so much the length) of the reset_code. It should be cryptographically random (i.e. os.urandom) so that an attacker cannot simply guess it. random.random and derived methods are not suitable.
As Jean-Paul pointed out, asking for both username and e-mail requires checking whether they both match the same user. Hence it is more common to ask for either username or e-mail, verifying they are in your database, and sending recovery link to appropriate address.
Storing the recovery token in session data will likely be cumbersome for some users, as phihag described. Such tokens are usually stored in regular database. Note, however, that they are password-equivalent: once obtained, they can be freely exchanged for a password. Because of that, they need to salted & hashed (in the same secure manner that is applied to passwords themselves) before storing in the database. This also means that your recovery handler must salt & hash the incoming token before searching for it in your database.
The best solution is use email address as username,then user just have to remember his email address.And you just only have to validate user's email address.

Categories

Resources