From 5a8cc0d7fc241494580cd4a060690eaf09ff46d7 Mon Sep 17 00:00:00 2001 From: Frederick Muriuki Muriithi Date: Wed, 8 Mar 2023 11:18:35 +0300 Subject: Replace Bcrypt with Argon2 for better security. Bcrypt is now somewhat vulnerable to offline cracking, so we move our password hashing over to Argon2. --- README.md | 12 ++++++------ gn3/auth/authentication/users.py | 12 +++++++++--- main.py | 4 ++-- mypy.ini | 6 ++++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f328e87..1026ac4 100644 --- a/README.md +++ b/README.md @@ -194,17 +194,17 @@ following environment variable(s): To run tests: ```bash -$ export AUTHLIB_INSECURE_TRANSPORT=true -$ export OAUTH2_ACCESS_TOKEN_GENERATOR="tests.unit.auth.test_token.gen_token" -$ pytest +export AUTHLIB_INSECURE_TRANSPORT=true +export OAUTH2_ACCESS_TOKEN_GENERATOR="tests.unit.auth.test_token.gen_token" +pytest ``` To specify unit-tests: ```bash -$ export AUTHLIB_INSECURE_TRANSPORT=true -$ export OAUTH2_ACCESS_TOKEN_GENERATOR="tests.unit.auth.test_token.gen_token" -$ pytest -k unit_test +export AUTHLIB_INSECURE_TRANSPORT=true +export OAUTH2_ACCESS_TOKEN_GENERATOR="tests.unit.auth.test_token.gen_token" +pytest -k unit_test ``` Running pylint: diff --git a/gn3/auth/authentication/users.py b/gn3/auth/authentication/users.py index 54838a3..5ee148f 100644 --- a/gn3/auth/authentication/users.py +++ b/gn3/auth/authentication/users.py @@ -2,7 +2,8 @@ from uuid import UUID, uuid4 from typing import Any, Tuple, NamedTuple -import bcrypt +from argon2 import PasswordHasher +from argon2.exceptions import VerifyMismatchError from gn3.auth import db from gn3.auth.authorisation.errors import NotFoundError @@ -60,7 +61,11 @@ def valid_login(conn: db.DbConnection, user: User, password: str) -> bool: if row is None: return False - return bcrypt.checkpw(password.encode("utf-8"), row["password"]) + hasher = PasswordHasher() # TODO: Maybe tune the parameters here... + try: + return hasher.verify(row["password"], password) + except VerifyMismatchError as _vme: + return False def save_user(cursor: db.DbCursor, email: str, name: str) -> User: """ @@ -79,7 +84,8 @@ def save_user(cursor: db.DbCursor, email: str, name: str) -> User: def set_user_password( cursor: db.DbCursor, user: User, password: str) -> Tuple[User, bytes]: """Set the given user's password in the database.""" - hashed_password = bcrypt.hashpw(password.encode("utf8"), bcrypt.gensalt()) + hasher = PasswordHasher() # TODO: Maybe tune the parameters here... + hashed_password = hasher.hash(password) cursor.execute( ("INSERT INTO user_credentials VALUES (:user_id, :hash) " "ON CONFLICT (user_id) DO UPDATE SET password=:hash"), diff --git a/main.py b/main.py index 6dadac2..49e5d55 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ from datetime import datetime import click -import bcrypt +from argon2 import PasswordHasher from yoyo import get_backend, read_migrations from gn3 import migrations @@ -37,7 +37,7 @@ def __init_dev_users__(): "password": "testpasswd"},) def __hash_passwd__(passwd): - return bcrypt.hashpw(passwd.encode("utf8"), bcrypt.gensalt()) + return PasswordHasher().hash(passwd) with db.connection(app.config["AUTH_DB"]) as conn, db.cursor(conn) as cursor: cursor.executemany(dev_users_query, dev_users) diff --git a/mypy.ini b/mypy.ini index 52ea938..a4ae96e 100644 --- a/mypy.ini +++ b/mypy.ini @@ -63,3 +63,9 @@ ignore_missing_imports = True [mypy-email_validator] ignore_missing_imports = True + +[mypy-argon2] +ignore_missing_imports = True + +[mypy-argon2.*] +ignore_missing_imports = True \ No newline at end of file -- cgit v1.2.3