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.
I am using admin-SDK of firebase in python
I have created a new user using the following code
user = auth.create_user(
email=email,
email_verified=False,
password=password,
display_name=name,
disabled=False)
now I have created a function that takes name , _email id _ and password from the user and fetch user using it's email id and then checks if entered details are correct.
def check_user(self, name, email, password): # fixme compare password
user = auth.get_user_by_email(email)
if user.display_name == name and user.email == email:# add password comparision
print('successful login')
return True
else:
print('username or password incorrect')
return False
I want to compare password entered with the password stored, but I am unable to compare as I can't access password, I can only access passwordHash using user.passwordHash and passwordSalt using user.passwordSalt.
is there any away I can find passwordHash or passwordSalt of password so I can compare the hashes.
The usual flow when using Firebase Authentication is that your users sign in with client-side code that uses a Firebase SDK directly. So in that case,
Firebase itself would be performing the check whether the password is correct.
You can perform the check yourself, but you'll have to hash the plaintext password from the user in your code and then compare the stored and calculated hash values, essentially duplicating what Firebase already does. Firebase uses a modified version of scrypt to encrypt the passwords.
There's a library called pyrebase. You can use it to mimic client in server. Simply use sign_in_with_email_and_password(email, password) once you initiate the pyrebase object.
GitHub url: https://github.com/thisbejim/Pyrebase
Firebase Admin SDK for python do not provide a way to compare password. However, there is a solution to confirm user authenticity.
This might be helpful https://stackoverflow.com/a/71398321/9681645
I am running a website using Flask microframework and sqlite3 to store user logins. Currently, I am having trouble with matching the username and password entered by the user from an HTML form, with the existing records within my database.
I am using the flask-login extension to help me with this, and when I try and match, I am receiving a TypeError:
list indices must be integers or slices, not str
here is my python code that is turning the SQLite table into a variable:
con = sql.connect("table.db")
cur = con.cursor()
cur.execute('SELECT * FROM users')
names = cur.fetchall()
I then have this code which is taking the password from the HTML form, and trying to match it with the password linked to the username in the table
user_name = request.form['username']
if request.form['password'] == names[user_name]['password']:
user = User()
user.id = user_name
flask_login.login_user(user)
this is what 'names' returns:
[(7, 'ValidName', 'ValidTest', 'User#test.com'), (8, 'User2', 'password2', 'User#test2.com')]
What needs to happen is the program will check the form input for 'password' and will match it with the 'password' that is related to the username. So as an example, if ValidName and ValidTest were entered into the form, they would be requested by the program, and matched with the records found in 'names'.
I assume you have not hashed your password which is something you should do. Without security in mind
here is my dirty approach
cur.execute('SELECT * FROM users WHERE name = %s AND password = %s', (request.form['username'], request.form['password']))
user = cur.fetchone()
This can be helpful
Here is the guilty: names[user_name]['password']
names is the return value of fetchall and hence is a plain list. To use it in above expression, it should be a mapping of mappings.
You should construct it that way:
names = {row[1]: {'id': row[0], 'password': row[2], 'mail': row[3]}
for row in cur.fetchall()}
But beware: this loads the full user database in memory. It only makes sense if you have few users...
I was finishing up a simple user login with Flask and flask-Bcrypt. However, when trying to login with a user that is stored in my database, I keep getting this error
ValueError: Invalid salt
models.py
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
email = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
posts = db.relationship("Post", backref="author", lazy="dynamic")
def __init__(self, name, email, password):
self.name = name
self.email = email
self.password = bcrypt.generate_password_hash(password)
def __repr__(self):
return '<User {}>'.format(self.name)
views.py
#app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter(User.name == form.username.data).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
flash("you were just logged in!")
login_user(user)
return redirect(url_for("home"))
else:
flash("bad username or password")
return render_template("login.html", form=form)
forms.py
class LoginForm(Form):
username = StringField('username', validators=[DataRequired()])
password = PasswordField('password', validators=[DataRequired()])
My problem is similar to described by #tomClark
I use Postgres as my DDBB and his driver, or the DDBB system, encode always an already encoded string. The second encode process create an invalid hash like this:
'\\x24326224313224483352757749766438764134333757365142464f4f4f464959664d66673575467873754e466250716f3166375753696955556b2e36'
A correct hash looks like this:
$2b$12$Wh/sgyuhro5ofqy2.5znc.35AjHwTTZzabz.uUOya8ChDpdwvROnm
To resolve it, I decode the hash to utf8 first than save it to the DDBB.
Example code:
def set_password(self, pw):
pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt())
self.password_hash = pwhash.decode('utf8') # decode the hash to prevent is encoded twice
In my case, the problem was related to a type conversion going on during password storage. Using bcrypt.generate_password_hash(plaintext) returns a binary value, like b'$2b$12$zf/TxXZ4JJZ/lFX/BWALaeo0M.wNXrQXLqIFjmZ0WebqfVo9NES56'.
Like mine was, your password column is set up as a string:
password = db.Column(db.String, nullable=False)
I found that generating the hash above, storing that binary value it in my string password column, then simply retrieving it resulted in a different value due to SQLAlchemy's type conversion - nothing to do with bcrypt at all!
A question on correct column type helped me realise that for correct roundtrip I had to store passwords as binary. Try replacing your column definition with:
password = db.Column(db.Binary(60), nullable=False)
I don't know for certain but suggest that different production environments and databases might handle this type conversion differently (reversibly in some cases, not in others), perhaps explaining the mixed success #Samuel Jaeschke has had.
This also explains why encoding the input string to a constrained character set (an earlier solution) might help in some cases and not others - if it causes the to/from type conversion to work then you'll recover the correct hash from the database for comparison.
At any rate, that solved this problem for me.
You need to apply .decode('utf-8') to your self.password:
def set_password(self, password):
"""Set password."""
self.password = bcrypt.generate_password_hash(password).decode('utf-8')
It appears that this exception will also be returned if anything goes wrong while hashing a password.
From the bcrypt source for hashpw():
hashed = _bcrypt.ffi.new("unsigned char[]", 128)
retval = _bcrypt.lib.crypt_rn(password, salt, hashed, len(hashed))
if not retval:
raise ValueError("Invalid salt")
The bcrypt package (which Flask-Bcrypt uses to get the work done) returns ValueError: Invalid salt whenever the call to the OS's bcrypt lib returns an error. So if for some reason it is unable to invoke the bcrypt lib at all, it will still (incorrectly) return the Invalid salt error.
Seems to be a flaw in the bcrypt package implementation - it should check for specific values of retval.
In my case, the error turned out to be related running Flask under Apache mod_wsgi in a virtualenv. I could run flask directly without problems (using flask-cli), but the exact same app instance wouldn't successfully use bcrypt when running under mod_wsgi.
The problem was solved by modifying my Apache config to use the virtualenv as the main Python environment for mod_wsgi.
In httpd.conf or under /etc/httpd/conf.d/... add:
WSGIPythonHome /path/to/my/application-virtualenv
More information about this configuration can be found here: Virtual Environments — mod_wsgi documentation
I still suspect that my particular problem is related to something being shadowed by my system's python site-packages, or something else related to python includes.
Edit: Setting WSGIPythonHome turned out not to fix the problem. In the end I switched to uWSGI with nginx.
Basically you would like to encode your data before the hash: password.encode('utf-8'). If it comes as unicode it may raise errors.
Have a look here also: https://github.com/maxcountryman/flask-bcrypt/issues/9
I believe you are using python 3 and bcrypt0.7.1. first you have to delete the users in your database, then go to your models and add .decode('utf-8') to the generate_password_hash() method like so:
pw_hash = bcrypt.generate_password_hash(‘hunter2’).decode('utf-8')
Alternatively you can uninstall flask-bcrypt==0.7.1 and install flask-bcrypt==0.62. Make sure you delete the users from the tables before installing flask-bcrypt==0.62
I had a similar problem. My code for checking the password was as follows:
if check_password_hash(form.password.data, user.pw_hashed):
When i reversed the order to:
if check_password_hash(user.pw_hashed, form.password.data):
It worked well.
I had the same problem.
It turned out that the username and password combination I was trying to check was not hashed in the first place.
Make sure that the password for the username you are trying to check is already hashed and not plain text.
If the password is saved in plain text not hashed, you will get this error.
I had a similar problem - got an: ValueError: Invalid salt - it turned out that in my models I had too few characters in my column:
password = Column(String(20))
In my database and models I had to change it to:
password = Column(String(100))
and it worked.
i found my own solution (postgresql):
use bytea data type for password.
when write password to db, use convert_to
when read password from db, use convert_from
You completely don't need flask-bcrypt for using bcrypt.
Just do something like this:
class User(Base):
_password = db.Column("password", db.String, nullable=False)
#hybrid_property
def password(self):
return self._password
#password.setter
def password(self, value):
bvalue = bytes(value, 'utf-8')
temp_hash = bcrypt.hashpw(bvalue, bcrypt.gensalt())
self._password = temp_hash.decode('utf-8')
def check_password(self, value):
return bcrypt.checkpw(value.encode('utf-8'), self._password.encode('utf-8'))
thclark's answer - declare the password column as a binary type - is the most correct, but I thought I'd dig into what is happening, specifically with a Postgresql backend.
The problem is that the password hash generated by flask-bcrypt, when saved in a SQLAlchemy String column, is mysteriously transformed at some point, such that when the value retrieved from the database is passed to flask-bcrypt's check_password_hash function we get an Invalid Salt error.
The "transformation" occurs because it turns out that SQLAlchemy, as far as I can tell, does not require the values assigned to String or Unicode columns to be strings*. Instead, the value is ultimately passed to the DBAPI connector - let's assume it psycopg2 in this case - and the connector tries to adapt the value to fit into whatever SQL SQLAlchemy has generated.
Psycopg2 adapts binary values such as bytes by converting them to the Postgresql binary string representation. If the password column was declared as LargeBinary then the value would be round-tripped correctly. As it is, the binary string representation is stored in the String column. Thus b'$2b$10$0Sfngi1XzpgxDkZPVcaolOHYu3h6IcN.ZHE4E8lWj0RuMGuVUvkHO' becomes '\x243262243130243053666e676931587a706778446b5a505663616f6c4f48597533683649634e2e5a48453445386c576a3052754d47755655766b484f' in the database.
The binary string representation is essentially the bytes converted to hex, so converting between the two representations isn't too difficult:
>>> bs = b'$2b$10$0Sfngi1XzpgxDkZPVcaolOHYu3h6IcN.ZHE4E8lWj0RuMGuVUvkHO'
>>> s = '\\x243262243130243053666e676931587a706778446b5a505663616f6c4f48597533683649634e2e5a48453445386c576a3052754d47755655766b484f'
>>> bs.hex() == s[2:]
True
>>> bytes.fromhex(s[2:]) == bs
True
So, the hash is being converted into a value suitable for insertion into a Postgresql BYTEA column, so we should declare our model's password column as LargeBinary, or sqlalchemy.dialects.postgresql.BYTEA.
Encoding the password prior to hashing is redundant - flask-bcrypt does this automatically.
If you are stuck with the password column as a String then decoding the hash before writing to the database makes sense. It's probably sufficient to decode as ASCII.
* I don't know why SQLAlchemy takes this lenient approach. At a guess it's based on pragmatism: if you can insert bytes into VARCHAR columns using psycopg2, why should SQLAlchemy try to stop you? At least you get a warning if you try it on a Unicode column. Perhaps the arrival of type hints in SQLAlchemy 2.0 will change this behaviour.
I had a similar issue (invalid salt), but nobody here mentioned this solution.
Be aware with naming when creating a new bcrypt object:
As documentation states:
Namespacing Issues
It’s worth noting that if you use the format, bcrypt = Bcrypt(app) you are effectively overriding the bcrypt module. Though it’s unlikely you would need to access the module outside of the scope of the extension be aware that it’s overriden.
Alternatively consider using a different name, such as flask_bcrypt = Bcrypt(app) to prevent naming collisions.
In my case, I had unhashed password for some users and when I tried to login with the unhashed password users, the app crashed. Just look at your DB or use another user.
Make sure that the user you try to log in his password hashed in the database
if his password stored as plain text it will raise that error
For me, I was trying to explore flask, and ran into the same issue when validating the hash -
I was passing the raw sql to db to get the hashed password, and then check it. I realized after reading through here that sqlalchemy will return a tuple of values on query. So I just looped through it and then passed the array index to. It works, may be the issue was that we passed the tuple values to check_password. I am new to python so do let me know if I can improve or I have thought it correctly.
def post(self):
name = request.form['EmailField1']
secpass = request.form['password1']
src_session = initialize_db()[0]
query= ('select "Password" from login where "Email" =' )+("'%s'" ";") %(name)
user = src_session.execute(query)
users=user.fetchall()
for user in users:
authorized = password_hash.check_password(user[0],secpass)
if not authorized:
return {'error': 'Email or password invalid'}, 401
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