summaryrefslogtreecommitdiff
path: root/server/szurubooru/func/auth.py
diff options
context:
space:
mode:
authorReAnzu <anzu@reanzu.com>2018-02-24 23:45:00 -0600
committerrr- <rr-@sakuya.pl>2018-03-08 23:40:47 +0100
commit3f52aceca44bc52e0c8654f46c66073320109ae9 (patch)
tree8eb921ff35b1241f3eafef97db45c6e495f37338 /server/szurubooru/func/auth.py
parent7519e071e79a2cf51e41e35fce030749c71fc0cf (diff)
server/users: harden password hashes
- Changed password setup to use libsodium and argon2id (regular SHA256 hashing for passwords is inadequate as modern GPU's can hash generate billions of hashes per second). - Added code to auto migrate old passwords to the new password_hash if the existing password_hash matches either of the legacy password generation schemes (SHA1 or SHA256). - Added migration to support new password_hash format length - Added column password_revision. This field will default to 0, which all passwords will have till they're updated. After that each password hash method has a revision.
Diffstat (limited to 'server/szurubooru/func/auth.py')
-rw-r--r--server/szurubooru/func/auth.py48
1 files changed, 36 insertions, 12 deletions
diff --git a/server/szurubooru/func/auth.py b/server/szurubooru/func/auth.py
index 25c991c..c9740fe 100644
--- a/server/szurubooru/func/auth.py
+++ b/server/szurubooru/func/auth.py
@@ -1,7 +1,10 @@
+from typing import Tuple
import hashlib
import random
from collections import OrderedDict
-from szurubooru import config, model, errors
+from nacl import pwhash
+from nacl.exceptions import InvalidkeyError
+from szurubooru import config, model, errors, db
from szurubooru.func import util
@@ -16,22 +19,29 @@ RANK_MAP = OrderedDict([
])
-def get_password_hash(salt: str, password: str) -> str:
- ''' Retrieve new-style password hash. '''
+def get_password_hash(salt: str, password: str) -> Tuple[str, int]:
+ ''' Retrieve argon2id password hash. '''
+ return pwhash.argon2id.str(
+ (config.config['secret'] + salt + password).encode('utf8')
+ ).decode('utf8'), 3
+
+
+def get_sha256_legacy_password_hash(salt: str, password: str) -> Tuple[str, int]:
+ ''' Retrieve old-style sha256 password hash. '''
digest = hashlib.sha256()
digest.update(config.config['secret'].encode('utf8'))
digest.update(salt.encode('utf8'))
digest.update(password.encode('utf8'))
- return digest.hexdigest()
+ return digest.hexdigest(), 2
-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) -> Tuple[str, int]:
+ ''' Retrieve old-style sha1 password hash. '''
digest = hashlib.sha1()
digest.update(b'1A2/$_4xVa')
digest.update(salt.encode('utf8'))
digest.update(password.encode('utf8'))
- return digest.hexdigest()
+ return digest.hexdigest(), 1
def create_password() -> str:
@@ -47,11 +57,25 @@ 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 pwhash.verify(
+ user.password_hash.encode('utf8'),
+ (config.config['secret'] + salt + password).encode('utf8'))
+ except InvalidkeyError:
+ possible_hashes = [
+ get_sha256_legacy_password_hash(salt, password)[0],
+ get_sha1_legacy_password_hash(salt, password)[0]
+ ]
+ if valid_hash in possible_hashes:
+ # Convert the user password hash to the new hash
+ new_hash, revision = get_password_hash(salt, password)
+ user.password_hash = new_hash
+ user.password_revision = revision
+ db.session.commit()
+ return True
+
+ return False
def has_privilege(user: model.User, privilege_name: str) -> bool: