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.
This commit is contained in:
ReAnzu 2018-02-24 23:45:00 -06:00
parent 838ced3aae
commit 0e5fbde097
4 changed files with 40 additions and 12 deletions

View file

@ -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

View file

@ -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:

View file

@ -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()

View file

@ -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)