Changing user's password using Flask and SQLite - python

I am trying to implement a function to change the user's password. I am storing only the hash in DB not the password and I want to ask the user to enter the old password first then the new one. I want to check if the old password's hash is matching the one stored in DB and if so, to update it with the new one. I am doing something wrong as the code is not passing the validation checks and I am having an error: "Invalid password".
Any help, would be appreciated.
#app.route("/change_password", methods=["GET", "POST"])
#login_required
def change_password():
user_id=session["user_id"]
if request.method=="POST":
password = request.form.get("password")
hash = generate_password_hash(password)
new_password = request.form.get("new_password")
confirm_new_password = request.form.get("confirm_new_password")
new_hash = generate_password_hash(new_password)
#Validations:
if not password:
return apology("must provide password", 400)
if not new_password:
return apology("must provide a new password", 400)
#Ensure password confirmation is provided
if not confirm_new_password:
return apology("must confirm new password", 400)
#Check if new password matches
if new_password!= confirm_new_password:
return apology("password does not match", 400)
# Query database for the current password
rows = db.execute("SELECT * FROM users WHERE hash = ?", hash)
# Ensure username exists and password is correct
if len(rows) != 1:
return apology("invalid password", 400)
#Update DB with the new_hash
else:
db.execute ("UPDATE users SET hash=:new_hash WHERE id=:id", new_hash = new_hash, id = user_id)
return redirect("/")
else:
return render_template("change_password.html")

There are quite some problems with your code...
The biggest problem
rows = db.execute("SELECT * FROM users WHERE hash = ?", hash)
You have to search for the user name, not for the hash!!! Imagine, two users have the same password, or a user enters the wrong password, but that is the password of another user...
I never used direct access to sqlite, only via sqlalchemy, but from a quick look at the docs ( https://docs.python.org/3/library/sqlite3.html#sqlite3.Cursor.executemany ) db.execute does not return e.g. rows, but you need to query it.
e.g. after your db.execute you need to do a db.fetchone or db.fetchall, see above linked documentation.
Also, the order of your code could be improved.
e.g. first, you generate the hash of a new password, and then afterwards you check whether there is a password at all - this should be switched.
Speaking of validation, much of it could be done via e.g. flask-wtforms, so you don't need so much validation code on your own.

Related

checking a variable against a record in a sqlite3 database to see if data entered is unique

so I am trying to create a function that allows a user to create a profile with personal information, in this they will enter a username that will act as primary key and requires to be unique, so when entering this username I am trying to check the data entered to see if it already exists in the sqlite3 database, if it does, the user is asked to try another username, if not the function will continue.
I was certain this should work because i used similar code to check values entered when a user logs in in a login function i coded, so i am quite stumped.
Any help would be greatly appreciated...
the code in question:
def signupInfo():
#takes input for username and checks it is alphanumeric, username will also act as primary key so a validation for it being unique will occur
username = input("Choose username: ")
#check for if the username is unique
uniqueUserCheck = '''SELECT * FROM users WHERE `username` = ?'''
cursor.execute(uniqueUserCheck, [username])
user = cursor.fetchone()
if user is not None:
username = input("Choose a unique user: ")
else:
#check if username is alphanumeric or has enough characters
while username.isalpha() == False or len(username) <= 3:
username = input("invalid username, try again: ")

Accessing specific item in sqlite3 database when user and password matches

I've created a login GUI with sqlite in python and i've got it working correctly, however I want it to do something else as well when it logs in.
My sqlite database currently has the following columns;
Username, Email, Password, Workstation, Directory
I've found that you can take an item string in the table by using something like;
connection.fetchall()[index]
However, I don't know how to implement it into my code to check for the user that logged in.
username = "username"
password = "password"
# database information
connection = sqlite3.connect(databasepath+'\login.db')
result = connection.execute("SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?", (username, password))
if (len(result.fetchall()) > 0):
#some code#
I want to access the 'Directory' item for the user that logged in

Is it possible to deny access to copied password field in database?

My Django app password in database looks like this:
pbkdf2_sha256$100000$XXXXXXXXXX$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
But if I duplicate it to another user, I could log in to that user's account.
Assuming a database breach or some kind of injection, can I detect if password was somehow duplicated/copied so that I can deny access to the account or alert admins?
as Selcuk says if some one has access to write into your database, he/she can do anything like generate password as your system want.
but you can make it harder to change password.
if you want to deny password copy from database you must create your own user model and update hashing algorithm like this.
first create a user model:
from django.contrib.auth.hashers import make_password, check_password
class MyUser(User):
def set_password(self, raw_password):
self.password = make_password(self.username + raw_password)
self._password = raw_password
def check_password(raw_password):
def setter(raw_password):
self.set_password(raw_password)
self._password = None
self.save(update_fields=['password'])
return check_password(self.username + raw_password, self.password. setter)
by these changes user password hashes are contain username and copy hash for other user does not works correctly

sha256_crypt.encrypt always returning another hash

I am developing a webapp using python and flask. It has a user system, so, of course, a registration form. I am using, to encrypt the password of the user that wants to registrate, passlib.hash.sha256 . Here's what I am doing:
from passlib.hash import sha256_crypt as sha256
[...]
if request.method == "POST" and form.validate():
username = request.form['username']
password = request.form['password']
confirm_password = request.form['confirm_password']
email = request.form['email']
password = sha256.encrypt(password) #Encryption.
c, conn = connection('accounts') #Connection to the database
x = c.execute("SELECT * FROM accounts WHERE username = '%s' OR email = '%s'" %(thwart(username), thwart(email)))
if x:
flash("We are very sorry, but this Username/Email-address is already taken. Please try again")
else:
c.execute('INSERT INTO accounts VALUES ("%s", "%s", "%s")' %(thwart(username), thwart(password), thwart(email)))
conn.commit()
flash('Succesfully Registered!')
In the database, the hash is always varying, even if the same password has been inputed. Does anybody know why? What am I doing wrong?
Fristly, please note that sha256_crypt.encrypt(..) is deprecated since version 1.7 and is instead renamed to sha256_crypt.hash(..) so you have
hash = sha256_crypt.hash("password")
for creating the hash. As the hash includes a random salt, you can't recalculate the hash and compare, instead you should lookup the hash in the table, and then use it in a sha256_crypt.verify() like:
sha256_crypt.verify("password", hash)
check your database structure.
sha256 may require upto 70 values.
Increase the password field to about 100 values.
Try pycrypto.sha256 instead, passlib seems not the the right solution for your requirement to compute an unsalted hash.

Django - revisiting "change password at next login”

I apologize, as this has been asked before (https://stackoverflow.com/a/5570717/3529404). However, I am having trouble with the accepted answer from user Chris Pratt. The code generally works - I am able to force password reset. However, where I am having trouble is trying to ensure that the new password is different than the old password. As the code is currently written, the same password is allowed.
From Chris's answer:
def password_change_signal(sender, instance, **kwargs):
try:
user = User.objects.get(username=instance.username)
if not user.password == instance.password:
profile = user.get_profile()
profile.force_password_change = False
profile.save()
except User.DoesNotExist:
pass
It seems as this should be checked in the line:
if not user.password == instance.password:
However, when I print user.password and instance.password (despite entering in the same password in both fields), the hashed value is not equal. Oddly enough, if I keep changing the password, the value that is printed for instance.password becomes the value for user.password on the next change.
Basically, all I want to do is use the code from the previous linked answer (https://stackoverflow.com/a/5570717/3529404), but enforce that the new password is different than the old password.
Thank you!!
UPDATE!
As discussed in the comments, I think my main area of frustration at the moment is not understanding exactly how user/instance differ. In particular, when print both user and instance passwords (see below), the hashed values are different even though the same password has been entered in each.
My code is slightly different than #Chris Pratt's because I am not using the depreciated user profile commands. Hopefully I didn't leave anything out!
webapp/models.py
class UserAdditional(models.Model):
user = models.ForeignKey(User, unique=True)
force_password_change = models.BooleanField(default=True)
def create_user_additional_signal(sender, instance, created, **kwargs):
if created:
UserAdditional.objects.create(user=instance)
def password_change_signal(sender, instance, **kwargs):
try:
user = User.objects.get(username=instance.username)
# these hashed values differ, even when the instance password entered is the same as the stored user password
print user.password
print instance.password
if not user.password == instance.password:
useradditional_obj = UserAdditional.objects.get(user=user)
useradditional_obj.force_password_change = False
useradditional_obj.save()
except User.DoesNotExist:
pass
signals.pre_save.connect(password_change_signal, sender=User, dispatch_uid='webapp.models')
signals.post_save.connect(create_user_additional_signal, sender=User, dispatch_uid='webapp.models')
webapp/middleware.py
class PasswordChangeMiddleware:
def process_request(self, request):
if request.user.is_authenticated() and not re.match(r'^/password_change/?', request.path) \
and not re.match(r'^/logout/?', request.path):
useradditional_obj = UserAdditional.objects.get(user=request.user)
if useradditional_obj.force_password_change:
return HttpResponseRedirect('/password_change/')
webapp/forms.py --- for password requirement enforcing
class ValidatingPasswordForm(object):
MIN_LENGTH = 8
def clean_new_password1(self):
password1 = self.cleaned_data.get('new_password1')
# At least MIN_LENGTH long
if len(password1) < self.MIN_LENGTH:
raise forms.ValidationError("The new password must be at least %d characters long." % self.MIN_LENGTH)
# check numbers and special characters
nums = len(set(re.findall(r'[0-9]',password1)))
symb = len(set(re.findall(r'[~!##$%^&\*()_+=-`]',password1)))
if nums <= 0 or symb <= 0:
raise forms.ValidationError("The new password must contain at least one number and one special character [~!##$%^&\*()_+=-`]")
return password1
class ValidatingPasswordChangeForm(ValidatingPasswordForm, auth.forms.PasswordChangeForm):
pass
class ValidatingSetPasswordForm(ValidatingPasswordForm, auth.forms.SetPasswordForm):
pass
It is generally a good practice to require an authenticated user provide the current password when changing passwords. This protects against the case, even if unlikely, that a logged in user leaves the workstation with an active session and some "evil" user attempts to hijack their account by changing the password.
By requiring the user to enter both the old and new passwords you can also prevent password re-use both on the client and server side. This allows for increased usability for your users since you can warn them and disable submission of the form using JavaScript. By capturing the old and new passwords, you can pass both to the server and verify against re-use similar to the answer provided by warath-coder.
Update
As you've mentioned Django saved the hashes and not the actual passwords, and as a further protection the passwords are salted, see the Django docs on how passwords are stored. Because of this, you will not be able to simply compare the hashes. You can test that the old and new passwords match in the clean_new_password1 method of your form using the form data prior to the User object being updated. This can be done by simple comparison or by trying to authenticate with the old password as warath-coder has described.
I would do this:
def password_change_signal(sender, instance, **kwargs):
try:
user = authenticate(username=instance.username, password=instance.password)
if user is None: # means auth failed, which means password is not the same as the current password.
user = User.objects.get(username=instance.username)
user.set_password(instance.password)
user.save()
profile = user.get_profile()
profile.force_password_change = False
profile.save()
except User.DoesNotExist:
pass
basically i try to authenticate the user with the password they supply, and it should fail if the new password is different than the current.

Categories

Resources