From 0e5fbde09740c33a26c155787b2d8237ec52f82e Mon Sep 17 00:00:00 2001 From: ReAnzu Date: Sat, 24 Feb 2018 23:45:00 -0600 Subject: [PATCH] Changed password setup to use libsodium and argon2id * regular SHA256 is not secure * added code to auto migrate old passwords to the new password_hash if the existing password_hash matches either of the old password generation schemes. --- server/requirements.txt | 1 + server/szurubooru/func/auth.py | 39 ++++++++++++++++++++++------- server/szurubooru/migrations/env.py | 10 ++++++-- server/szurubooru/model/user.py | 2 +- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/server/requirements.txt b/server/requirements.txt index 2cd15ec1..7cc47868 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -11,3 +11,4 @@ scipy>=0.18.1 elasticsearch>=5.0.0 elasticsearch-dsl>=5.0.0 scikit-image>=0.12 +pynacl>=1.2.1 \ No newline at end of file diff --git a/server/szurubooru/func/auth.py b/server/szurubooru/func/auth.py index 25c991c4..d2bb69ac 100644 --- a/server/szurubooru/func/auth.py +++ b/server/szurubooru/func/auth.py @@ -1,8 +1,11 @@ import hashlib import random from collections import OrderedDict -from szurubooru import config, model, errors +from nacl.exceptions import InvalidkeyError + +from szurubooru import config, model, errors, db from szurubooru.func import util +from nacl.pwhash import argon2id, verify RANK_MAP = OrderedDict([ @@ -17,7 +20,14 @@ RANK_MAP = OrderedDict([ def get_password_hash(salt: str, password: str) -> str: - ''' Retrieve new-style password hash. ''' + """ Retrieve argon2id password hash.""" + return argon2id.str( + (config.config['secret'] + salt + password).encode('utf8') + ).decode('utf8') + + +def get_sha256_legacy_password_hash(salt: str, password: str) -> str: + """ Retrieve old-style sha256 password hash.""" digest = hashlib.sha256() digest.update(config.config['secret'].encode('utf8')) digest.update(salt.encode('utf8')) @@ -25,8 +35,8 @@ def get_password_hash(salt: str, password: str) -> str: return digest.hexdigest() -def get_legacy_password_hash(salt: str, password: str) -> str: - ''' Retrieve old-style password hash. ''' +def get_sha1_legacy_password_hash(salt: str, password: str) -> str: + """ Retrieve old-style sha1 password hash.""" digest = hashlib.sha1() digest.update(b'1A2/$_4xVa') digest.update(salt.encode('utf8')) @@ -47,11 +57,22 @@ def create_password() -> str: def is_valid_password(user: model.User, password: str) -> bool: assert user salt, valid_hash = user.password_salt, user.password_hash - possible_hashes = [ - get_password_hash(salt, password), - get_legacy_password_hash(salt, password) - ] - return valid_hash in possible_hashes + + try: + return verify(user.password_hash.encode('utf8'), + (config.config['secret'] + salt + password).encode('utf8')) + except InvalidkeyError: + possible_hashes = [ + get_sha256_legacy_password_hash(salt, password), + get_sha1_legacy_password_hash(salt, password) + ] + if valid_hash in possible_hashes: + # Convert the user password hash to the new hash + user.password_hash = get_password_hash(salt, password) + db.session.commit() + return True + + return False def has_privilege(user: model.User, privilege_name: str) -> bool: diff --git a/server/szurubooru/migrations/env.py b/server/szurubooru/migrations/env.py index 7065a69e..61b20f4e 100644 --- a/server/szurubooru/migrations/env.py +++ b/server/szurubooru/migrations/env.py @@ -35,7 +35,11 @@ def run_migrations_offline(): ''' url = alembic_config.get_main_option('sqlalchemy.url') alembic.context.configure( - url=url, target_metadata=target_metadata, literal_binds=True) + url=url, + target_metadata=target_metadata, + literal_binds=True, + compare_type=True + ) with alembic.context.begin_transaction(): alembic.context.run_migrations() @@ -56,7 +60,9 @@ def run_migrations_online(): with connectable.connect() as connection: alembic.context.configure( connection=connection, - target_metadata=target_metadata) + target_metadata=target_metadata, + compare_type=True + ) with alembic.context.begin_transaction(): alembic.context.run_migrations() diff --git a/server/szurubooru/model/user.py b/server/szurubooru/model/user.py index dd7c0629..d355a143 100644 --- a/server/szurubooru/model/user.py +++ b/server/szurubooru/model/user.py @@ -23,7 +23,7 @@ class User(Base): last_login_time = sa.Column('last_login_time', sa.DateTime) version = sa.Column('version', sa.Integer, default=1, nullable=False) name = sa.Column('name', sa.Unicode(50), nullable=False, unique=True) - password_hash = sa.Column('password_hash', sa.Unicode(64), nullable=False) + password_hash = sa.Column('password_hash', sa.Unicode(128), nullable=False) password_salt = sa.Column('password_salt', sa.Unicode(32)) email = sa.Column('email', sa.Unicode(64), nullable=True) rank = sa.Column('rank', sa.Unicode(32), nullable=False)