Being a newb to python I am not quite sure why I am getting inconsistent results.
I register a user and the password in my table ends up being the hashed version. When the user updates his password, the password in the table ends up being the unhashed version. Obviously, I want the hashed version. What am I doing wrong? (I am using SQLAlchemy and mysql if that matters.)
I have the following:
def hash_password(password):
blah, blah, blah # hash my password here
return hashed_password
class User(Base):
__tablename__ = 'mytable'
email = Column('email')
_password = Column('password')
def _get_password(self):
return self._password
def _set_password(self, password):
self._password = hash_password(password)
password = property(_get_password, _set_password)
password = synonym('_password', descriptor=password)
def __init__(self, password="", email=""):
self.email = email
self.password = password
#classmethod
def register(cls, email, password):
return DBSession.add(User(email=email,password=password)) # this correctly hashes the password
#classmethod
def update(cls, email, password):
return DBSession.query(cls).filter(cls.email == email).update({'password': password}) #password ends up being the unhashed password
The issue here is the way that you are updating the password via your User.update method. This method is skipping the ORM entirely and updating the row directly in the database. It should be obvious that the code to hash the password will not run when you do this. The User model that you pasted is just fine and similar to what I use. You need to use it though. This means that to update a password you should load the user, and set their password.
user = DBSession.query(User).filter_by(email=email).first()
if user:
user.password = new_password
and later when the transaction is committed things will be the way you expect.
You should store password hash in database, so field of your model must contain hash value, not raw password. To set password, you should use methods, that makes hashing and set hash to instance. To check, if password is correct, you should hash user-defined password and compare result with hash stored in your instance. Yo will not be able to decode password from hash - it's unsecure.
class User(Base):
__tablename__ = 'user'
email = Column('email', String(80))
password = Column('password', String(80))
def set_password(raw_password):
self.password = hash(raw_password)
def check_password(raw_password):
return self.password == hash(raw_password)
Related
I am currently trying to use bcrypt to encrypt/hash my passwords from my seeds and store them in MYSQL but it keeps giving me the same password. I am using python. Any help would be appreciated!
User.py
from app.db import Base
from sqlalchemy.orm import validates
from sqlalchemy import Column, Integer, String
salt = bcrypt.gensalt()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50), nullable=False)
email = Column(String(50), nullable=False, unique=True)
password = Column(String(200), nullable=False)
#validates('email')
def validate_email(self, key, email):
# make sure email address contains # character
assert '#' in email
return email
#validates('password')
def validate_password(self, key, password):
assert len(password) > 4
# encrypt password
return bcrypt.hashpw(password.encode('utf-8'), salt)
seeds.py
from app.models import User
from app.db import Session, Base, engine
# drop and rebuild tables
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
db = Session()
# insert users
db.add_all([
User(username='alesmonde0', email='nwestnedge0#cbc.ca', password='password123'),
User(username='jwilloughway1', email='rmebes1#sogou.com', password='password123'),
User(username='iboddam2', email='cstoneman2#last.fm', password='password123'),
User(username='dstanmer3', email='ihellier3#goo.ne.jp', password='password123'),
User(username='djiri4', email='gmidgley4#weather.com', password='password123')
])
db.commit()
db.close()
Assuming that:
you have copied the code exactly as in your original file
and that the “keeps giving me the same password” means that in database the open text password gets saved and not the hash from validators
If both above is correct, the issue is with the identation, i.e. the “validate_password” method is not in the User class at all.
Try to ident it properly, it should trigger and hash the password.
it keeps giving me the same password
You pass the same password and salt every single time:
>>> salt = bcrypt.gensalt()
>>> bcrypt.hashpw('password123'.encode('utf-8'), salt)
b'$2b$12$L14/6UZsC4YymGUiQgBxCO5c6YoHEFDSM9ZSvBW0CgO9YkRUGkXwW'
>>> bcrypt.hashpw('password123'.encode('utf-8'), salt)
b'$2b$12$L14/6UZsC4YymGUiQgBxCO5c6YoHEFDSM9ZSvBW0CgO9YkRUGkXwW'
If you want the same plaintext to result in different hashes using bcrypt, regenerate the salt each time you generate a hash (as you should be per best practice):
>>> bcrypt.hashpw('password123'.encode('utf-8'), bcrypt.gensalt())
b'$2b$12$e1.vrDabeTDcqjqJ3Wj1fuapoGBgRaTjYNEn.v1WvuBbQLIsNlS3O'
>>> bcrypt.hashpw('password123'.encode('utf-8'), bcrypt.gensalt())
b'$2b$12$jqE4jMUeGfTLYixrR5iB0OAWSM/ZIEPiscX5fPLcxn8rOHqzJOUt6'
The database looks like this:
class Users(UserMixin,db.Model):
__tablename__ = "users"
id = db.Column("id", db.Integer, primary_key=True)
username = db.Column(db.String(100))
hash_password = db.Column(db.Text)
secret_content = db.Column(db.Text)
The secret content is highly confidential, I don't even want the admin to know the content of the data.
My idea was to encode the content like this:
class Users(UserMixin,db.Model):
__tablename__ = "users"
id = db.Column("id", db.Integer, primary_key=True)
username = db.Column(db.String(100))
hash_password = db.Column(db.Text)
secret_content = db.Column(db.Text)
def __init__(self, username , password_hash, secret_content, key):
cipher_suite = Fernet(key)
self.username = username
self.secret_content = cipher_suite.encrypt(bytes(secret_content))
self.hash_password = password_hash
The key used to encrypt the data should be different for each user.
I wanted to create the key by hashing the password with sha256.
However, the hash is already stored in the user for login purposes.
Therefore I would use another hashing algorithm, MD5 for example.
The issue I see by doing that is that if a hacker is able to find/decypher this hash then he would be able to also extract the real password because at that point you can eliminate a lot of possibilities when the hacker brute forces the password.
Do I have other options or will I need to ask the user for a second unrelated password?
Based on the comments from #Artjom B.
Add salt to the key.
Encrypt key with PBKDF2 to encode the personal data with.
Encrypt the same key with sh256 for user login.
I'm working on Django project and I want to have two different entities in database - (default Django) User and Doctors. I want to have stored password in both entities.
def post(self, request, pk):
username = Doctor.objects.get(pk=a).email
password = Doctor.objects.get(pk=a).password
user = User.objects.create_user(username, username, password)
user.save()
return redirect('ps:index')
Atribute in forms.py for DoctorForm:
password = forms.CharField(widget=forms.PasswordInput)
But this is not working for passwords. I assume that the reason is hashing and salt. How to solve it?
Any help would be appreciated.
The password stored in database is hashed. If you want to save a new password, use user.set_password(new_password) and user.save(). Then copy the user.password to another entity.
I have a custom user model based off of AbstractUser and, I use a custom UserManager, I don't know if theres anything special I have to do to get authenticate to work. I know the user is in the db because I can do objects.get(username, password) and it will return the object.
class PassThroughFarmerManager(PassThroughManagerMixin, UserManager):
use_in_migrations = False
class Farmer(FarmStateble, MapPointable, LastRequestStorable, AbstractUser):
last_irrigation_cycle = models.DateTimeField(auto_now_add=False, auto_now=False, null = True, blank=True)
objects = PassThroughFarmerManager.for_queryset_class(FarmerQuerySet)()
Here is an example of my console output,
>>> models.Farmer.objects.get(username='901e2ac5-9324-11e5-81bf-c42c0323e33a').password
u'1223'
>>> u = authenticate(username = '901e2ac5-9324-11e5-81bf-c42c0323e33a', password = '1223')
>>> u
>>> type(u)
<type 'NoneType'>
When you use MyUserModel.objects.create(...), the password is stored in the database in plain text. The call to authenticate does not work, because Django expects the password to be hashed in the database.
Therefore, when you create a user, you need to ensure that the password is hashed, rather than being stored in plain text in the database. You can do this by calling user.set_password('new_password').
The full example in the docs shows a create_user manager method that calls set_password when creating the user. You would then use MyUserModel.objects.create_user(...) instead of MyUserModel.objects.create(...).
class MyUserManager(BaseUserManager):
def create_user(self, email, date_of_birth, password=None):
"""
Creates and saves a User with the given email, date of
birth and password.
"""
if not email:
raise ValueError('Users must have an email address')
user = self.model(
email=self.normalize_email(email),
date_of_birth=date_of_birth,
)
user.set_password(password)
user.save(using=self._db)
return user
I am trying to register a user into a sqlite database.
My form contains a hidden field with a numerical value.
When I process the form, it returns the following error:
"The browser (or proxy) sent a request that this server could not understand."
Which apparently means a variable is not passed to the function.
How could I address the problem please?
I have tried changing the data type to int() in the backend before sending to the function, I have made the field visible etcetc. Nothing worked.
Funny enough: when I use the "print" function in the backend and disable the "User.regUser(email, username, password, rank)", their values shows in the console... so I would assume the variables contain the info I need to pass to the function.
Here is the code:
Views:
#app.route('/register', methods=['GET', 'POST'])
def register():
#if request.method == 'POST':encrypt
email = request.form['email']
username = request.form['username']
password = generate_password_hash(request.form['pass1'])
rank = int(request.form['rank'])
print(email, password, username, rank)
User.regUser(email, username, password, rank)
# db.session.add(User(email, username, password, "50"))
# db.session.commit()
return render_template('register.html')
Models:
from pjctBB.views import db
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String, nullable=False)
username = db.Column(db.String, nullable=False)
password = db.Column(db.String, nullable=False)
rank = db.Column(db.Integer, nullable=False)
def __init__(self, email, username, password, rank):
self.email = email
self.username = username
self.password = password
self.rank = rank
def regUser(self, email, username, password, rank):
db.session.add(User(email, username, password, rank))
db.session.commit()
Thanks a bunch!
From what I gather, you're trying to request from a form without first rendering it, meaning there is no form to get or post from. Try this:
#app.route('/register', methods=['GET', 'POST'])
def register():
if request.mthod == 'GET':
return render_template('register.html')
elif request.method == 'POST':
email = request.form['email']
username = request.form['username']
password = generate_password_hash(request.form['pass1'])
rank = int(request.form['rank'])
print(email, password, username, rank)
User.regUser(email, username, password, rank)
#db.session.add(User(email, username, password, "50"))
#db.session.commit()
return render_template('register.html')
That's assuming register.html exists.
Thank you all for your interest in my question. It got me thinking a bit :)
I asked my question slighly differently on SO and 2 users gave me a solution.
I ncase someone runs into a similar problem as mine, please look this thread for a possible solution:
Flask registration error: missing 1 positional argument