flask-bcrypt - ValueError: Invalid salt - python

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:
'\\x24326224313224483352757749766438764134333757365142464f4f4f464959664d66673575‌​467873754e466250716f3166375753696955556b2e36'
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

Related

Flask Mongoengine ValidationError Field is required on .save() but fields already exist in db

Problem: I get a ValidationError when trying to perform a .save() when appending a value to an EmbeddedDocumentListField because I am missing required fields that already exist on the document.
Note that at this point the User document has already been created as part of the signup process, so it already has an email and password in the DB.
My classes:
class User(gj.Document):
email = db.EmailField(required=True, unique=True)
password = db.StringField(required=True)
long_list_of_thing_1s = db.EmbeddedDocumentListField("Thing1")
long_list_of_thing_2s = db.EmbeddedDocumentListField("Thing2")
class Thing1(gj.EmbeddedDocument):
some_string = db.StringField()
class Thing2(gj.EmbeddedDocument):
some_string = db.StringField()
Trying to append a new EmbeddedDocument to the EmbeddedDocumentListField in my User class in the Thing2 Resource endpoint:
class Thing2(Resource):
def post(self):
try:
body = request.get_json()
user_id = body["user_id"]
user = UserModel.objects.only("long_list_of_thing_2s").get(id=user_id)
some_string = body["some_string"]
new_thing_2 = Thing2Model()
new_thing_2.some_string = some_string
user.long_list_of_thing_2s.append(new_thing_2)
user.save()
return 201
except Exception as exception:
raise InternalServerError
On hitting this endpoint I get the following error on the user.save()
mongoengine.errors.ValidationError: ValidationError (User:603e39e7097f3e9a6829f422) (Field is required: ['email', 'password'])
I think this is because of the .only("long_list_of_thing_2s")
But I am specifically using UserModel.objects.only("long_list_of_thing_2s") because I don't want to be inefficient in bringing the entire UserModel into memory when I only want to append something the long_list_of_thing_2s
Is there a different way I should be going about this? I am relatively new to Flask and Mongoengine so I am not sure what all the best practices are when going about this process.
You are correct, this is due to the .only and is a known "bug" in MongoEngine.
Unless your Model is really large, using .only() will not make a big difference so I'd recommend to use it only if you observe performance issues.
If you do have to keep the .only() for whatever reason, you should be able to make use of the push atomic operator. An advantage of using the push operator is that in case of race conditions (concurrent requests), it will gracefully deal with the different updates, this is not the case with regular .save() which will overwrite the list.

MongoEngine - Call function only when document is being created or specific field being set?

I'm not sure what is the best/pythonic way of having a User document that automatically hashes its password upon creating.
Consider the following mongoengine model :
class User(Document):
email = EmailField(required=True, primary_key=True)
name = StringField(required=True)
pswd = StringField(required=True)
def check_pswd(self, password):
return verify_password(password, self.pswd)
def hash_pswd(self, password):
return hash_password(password):
def save(self, *args, **kwargs):
self.pswd = self.hash_pswd(self.pswd)
super().save(*args, **kwargs)
When I create a user, it works fine :
user = User()
user.email = 'user#email.com'
user.pswd = 'password'
user.name = 'User'
user.save()
Buf if I update it, it will double hash its password, I don't want that.
#User wants to change his name
user = User.objects(email='user#email.com')
user.name = 'User 2'
user.save()
Is there a way for me to hash its password only when creating or changing the password?
Or maybe I should delegate the responsibility of hashing the password to the View/Controller?
I am not giving you the code sample, you can use Document.update() method which will update the fields that only has changed.
If you still wanna use the save method,
Then you can create a logic along the following lines.
Check if the user has changed the password(by comparing the existing stored hash and new hash if any)
If the new hash is different then user has changed the password in that case you can push an Document.update method.
If not, don't call update on that field.
Alternatively update in Mongoengine accepts a iterable, so you can simply create a list or a dictionary object and convinently choose to Remove the password hash field from it.
As for who should execute this i.e View / Controller, its a Design decision but I would rather keep Representation (GUI / Front End) seperate from logic, so I would delegate this to the controller or even more Intrinsically to the Object who is responsible for handling all database/network related tasks, this way it would be isolated and easy to modify. And would not complexify or slow the View objects process / thread
Link for update using an iterable like Dict.
stackoverflow question for Mongoengine update using iterable
Link discussing save methods deprecation (The Maintainer Has commented below as save method not being deprecated, so trust him/her and proceed at will on this link)
Mongoengine save method deprecated?
Link for update method in mongoengine.
Mongoengine Atomic Update

Querying information for HTTPBasicAuth

For my site for auth I'm using https://flask-httpauth.readthedocs.io/en/latest/ . Now I'm trying to make it that it's using data from database. To do that i created database named Users and created columns named username and password.
To get data from this table after defining its class as model I've made get_user functions which looks like it:
#staticmethod
def get_user():
query = (Users
.select())
user = []
for s in query:
user.append(s)
return user
(I'm not sure if it's correct)
Next I had to modify get_pw function but I also wasn't sure how to modify it so I made it look like it:
#auth.get_password
def get_pw(username):
if username in Users.get_user():
return users.get(Users.get_user())
return None
Now after running the site I get prompt to give login and password but those that I set up in my database doesn't seem to work so there must be a problem with get_pw function. Also I'm using peewee SQL to manage database : http://docs.peewee-orm.com/en/latest/peewee/querying.html
You can get rid of your get_user method since you are issuing a very large select query that fetches all records from user table. The get_pw can be redefined as:
def get_pw(username):
user = Users.get(Users.name == username) #assuming you have a username field in model
return user.password #assuming a password field
Also, its a good practice to define your model class as a singular noun rather than plural. So, its better to call it User rather than Users.
This'll help you get started in no time: http://docs.peewee-orm.com/en/latest/peewee/quickstart.html#quickstart

django, entering a hashed password in registration and storing it as such

I am working on openedx(works on django) and i need to create an api to register the user coming from a particular site, i am being given a hashed password not a normal one and i need to save it as so.
The problem here is that the openedx's registration function hashes the password that is being passed into it.
so is there a way in django to store a password/register a user without hashing the password.
Should i go for updating the user's credentials directly using
raw()
any help would be appreciated, thanks.
I would suggest to override method set set_password in user_model.
class MyUser(AbstractBaseUser):
# if you need to hash passwords for some users.
is_password_hashed = models.BooleanField(default=True)
...
def set_password(self, raw_password):
if self.is_password_hashed:
super(MyUser, self).set_password(raw_password)
else:
self.password = raw_password
If you want to store only non-hashed passwords:
class MyUser(AbstractBaseUser):
...
def set_password(self, raw_password):
self.password = raw_password
Or even override default user model set_password method.
It's as simple as:
from django.contrib.auth.models import User
User.objects.filter(username="myuser").update(password=hashed_password)
(remember passwords are stored as hashed values in the database)
The Open edX manage_user management command was recently updated to support this use case when creating a new user.
Example:
./manage.py lms --settings=devstack manage_user jane jane#example.com --initial-password-hash 'pbkdf2_sha256$20000$mRxYkenyBiH6$yIk8aZYmWisW2voX5qP+cAr+i7R/IrZoohGsRK2fy4E='
However, that command requires a very recent version of Open edX and it will not have any effect if the user account already exists.
As an alternative, you could set up SSO between the external app and Open edX using OAuth2, in which case there's no need for Open edX to store any password at all.

how to store default username and password for login system test in sqlalchemy

Trying to make a simple login system in Python using pyramid framework. What I want to be able to do now is for example if we're working in php we can easily open phpmyadmin and set a fields with username and password that we can use to test the login system.
How can I do this in python pyramid project using sqlalchemy
This is my model class where I created my table structure
from test2.models.meta import Base
from which model classes will inherit
from sqlalchemy import (
Column,
Integer,
Unicode, #<- will provide Unicode field
UnicodeText, #<- will provide Unicode text field
Text,
)
class Users(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Text, unique=True, nullable=False,default='admin')
password = Column(Text, nullable=False,default='password')
from pyramid.view import view_config
from test2.models.services.userservice import UserServices
from pyramid.httpexceptions import HTTPFound, HTTPNotFound
from pyramid.security import remember,forget
This is the view config
#view_config(route_name='auth',match_param='action=in',renderer='string',request_method='POST')
#view_config(route_name='auth', match_param='action=out', renderer='string')
def dashboard(request):
username=request.POST.get('username')
if username:
user=UserServices.by_name(username)
if user and user.verify_password(request.POST.get('password')):
return HTTPFound(location=request.route_url('home'))
else:
headers=forget(request)
else:
#return HTTPNotFound
headers=forget(request)
return HTTPFound(location=request.route_url('home'),headers=headers)
What I want to achieve is a simple login system which tests the username and password then redirects
There are two versions of the official Pyramid tutorial, "SQLAlchemy + URL Dispatch Wiki Tutorial", which show both the wrong and right way for storing passwords:
Latest (v.1.6)
Master (unreleased)
In the "latest" branch, you will find how to set passwords in cleartext. This is absolutely the wrong way to store passwords.
In the "master" branch, we hash passwords.
Always hash passwords with a strong cryptographic algorithm that already exists, like bcrypt, and never attempt to write your own. Also, to be pedantic, one "hashes" passwords and does not "encrypt" them. Hashing is a one-way only operation, whereas encrypting can be reversed (decrypting).

Categories

Resources