From 9491cfd5ddac9e4ef712d89ad10bf251f01aed66 Mon Sep 17 00:00:00 2001 From: Sindre Stephansen Date: Sun, 15 Mar 2020 21:12:52 +0100 Subject: [PATCH] Implement stricter password policy Fixes #22 --- src/app/views/register.py | 7 ++++- src/app/views/utils.py | 58 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/app/views/register.py b/src/app/views/register.py index ecd414f..2853052 100755 --- a/src/app/views/register.py +++ b/src/app/views/register.py @@ -1,6 +1,6 @@ import web from views.forms import register_form -from views.utils import get_nav_bar, csrf_protected +from views.utils import get_nav_bar, csrf_protected, password_weakness import models.register import models.user import bcrypt @@ -41,6 +41,11 @@ class Register: if models.user.get_user_id_by_name(data.username): return render.register(nav, register, "Invalid user, already exists.") + # Check password security + weakness = password_weakness(data.password, data.username) + if weakness is not None: + return render.register(nav, register, weakness) + password_hash = bcrypt.hashpw(data.password.encode('UTF-8'), bcrypt.gensalt()) models.register.set_user(data.username, password_hash, data.full_name, data.company, diff --git a/src/app/views/utils.py b/src/app/views/utils.py index 8b8a6d3..9b0650b 100755 --- a/src/app/views/utils.py +++ b/src/app/views/utils.py @@ -78,3 +78,61 @@ def csrf_protected(f): return f(*args, **kwargs) return decorated + + +def is_common_password(password): + """Helper function that checks various common passwords.""" + def common_sequences(n): + # Check sequences of the same number + for i in range(n): + for j in range(n): + yield ''.join([str(i) for _ in range(j)]) + + # Check incrementing sequences + for i in range(n): + # Starting at 0 + seq = ''.join([str(j) for j in range(i)]) + yield seq + # Starting at 1 + yield seq[1:] + + # Decrementing + # Starting at 0 + yield seq[::-1] + # Starting at 1 + yield seq[1::-1] + + common_passwords = [ + 'password', 'qwerty', 'iloveyou', '123123', 'abc123', 'admin', + 'passwrod', 'password1', 'beelance', 'beelance2' + ] + + if password in common_passwords or password in common_sequences(12): + return True + + return False + + +def password_weakness(password, username): + """ + Check if the password fulfills the password policy. + + The policy is: + - At least 8 characters, but not more than 70 (due to bcrypt) + - Does not overlap with the username + - Not a common password + + :param password: The password to check + :param username: The username of the user (used to check similarity) + :return: The most important weakness of the password, or None if it fulfills the policy + """ + if len(password) < 8: + return "The password must be at least 5 characters long." + elif len(password) > 70: + return "The password can't be longer than 70 characters." + elif password in username or username in password: + return "The password can't overlap with your username." + elif is_common_password(password): + return "The password is too common. Choose something more unique." + + return None