forms.py
class UserRegisterForm(forms.ModelForm):
password = forms.CharField(widget=forms.PasswordInput(render_value=True))
class Meta:
model = User
I have views.py to show the form instance and update the instance.
On edit the form data,all other field are showing the correct value,password field is showing the entire hashed value from database and not the entered password.
I don't know how to show the entered password instead of hashed value in database.
You can't do that, of course. It's hashed for a reason, which is to make it impossible to get the clear text value.
You must have noticed though that no website or application will ever show you your current password when you're updating: you usually have to enter it yourself for verification, then enter the new password twice.
Remember about security.
#app/hashers.py
from django.contrib.auth.hashers import BasePasswordHasher
from django.utils.datastructures import SortedDict
class DummyPasswordHasher(BasePasswordHasher):
algorithm = "dummy"
def encode(self, password, salt):
return "dummy$%s" % (password)
def verify(self, password, encoded):
algorithm, hash = encoded.split('$', 2)
return hash == password
def safe_summary(self, encoded):
algorithm, hash = encoded.split('$', 2)
return SortedDict([
('algorithm', algorithm),
('password', hash),
])
#settings.py
PASSWORD_HASHERS = (
'app.hashers.DummyPasswordHasher',
)
PREFERRED_HASHER = 'app.hashers.DummyPasswordHasher'
EDIT I've corrected spelling mistakes
Related
For my website, i am not planning to make it compulsory for the users to sign up. Instead, they can create a post and set a password for it in order to manage it. I can use the 'set_password' method django provides for the User model:
def set_password(self, raw_password):
import random
algo = 'sha1'
salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
hsh = get_hexdigest(algo, salt, raw_password)
self.password = '%s$%s$%s' % (algo, salt, hsh)
so that sorts out the storing password part. But this will hash the password (which is good) but now i am stuck on how to authenticate this?? So when someone wants to edit the instance of the model they have created, they first need to be authenticated. How do i go about doing that?
Thank you
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.
Here is my signup form,
class SignupForm(forms.ModelForm):
class Meta:
model = User
fields = ['first_name', 'last_name','username', 'email', 'password']
def clean_username(self):
username = self.cleaned_data.get('username')
email = self.cleaned_data.get('email')
if username and User.objects.filter(username=username).exclude(email=email).count():
raise forms.ValidationError('This username has already been taken!')
return username
This works well to check if there is same username presents or not. However it does not check for case insensitivity. If there is a username e.g. 'userone', then it also accepts a username with 'Userone'. Although it does not break any functionality, but looks very unprofessional.
My question is how can I check for case insensitive right in the forms, and raise error?
Sometimes I faced the same issue. Django considers username unique different in lower or upper case. Like if I enter John, it a unique username and if I enter john, it's a new username. I need to consider John and john not in the database. As simple as facebook do, both uppercase and lower case username name is same, unique.
So I achieve this just changing my signup code like this.
username = self.cleaned_data.get('username').lower()
Also, in my login code, I convert my username to lower.
So that, all time it saves username lower in the database and log in with lower case username. Although a user tries to login with upper case username, then it saves to the database by converting to lower case.
You can use __iexact here:
User.objects.filter(username__iexact=username).exclude(email=email).exists() # instead of count, used exists() which does not make any DB query
Simplest method I think is:
Use .exists() method, if true then validation error else return ❤
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
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.