Stop people using very common passwords

If a user chooses a very common password then an attacker could guess it
in relatively few attempts, circumventing the lockout.

CESG recommend blacklisting the most common passwords:

> …enforcing the requirement for complex character sets in passwords is
> not recommended. Instead, concentrate efforts on technical controls,
> especially:
>
> - defending against automated guessing attacks by either using account
>   lockout, throttling, or protective monitoring
> - blacklisting the most common password choices

How I made this list:

- went to the OWASP repository of security lists:
  https://github.com/danielmiessler/SecLists

- downloaded `10k_most_common.txt`, `twitter-banned.txt` and
  `500-worst-passwords.txt`

- filtered out any under 8 characters:
  ```
  sed -r '/^.{,7}$/d' passwords-twitter.txt > passwords-combined.txt
  sed -r '/^.{,7}$/d' passwords-500.txt >> passwords-combined.txt
  sed -r '/^.{,7}$/d' passwords.txt >> passwords-combined.txt
  ```

- filtered out any duplicates:
  ```
  cat passwords-combined.txt | awk '!x[$0]++' > passwords-combined-deduped.txt
  ```
This commit is contained in:
Chris Hill-Scott
2016-09-27 11:28:12 +01:00
parent 0c704c246d
commit 136662bd30
5 changed files with 2106 additions and 8 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ import re
from wtforms import ValidationError
from notifications_utils.template import Template
from app.utils import Spreadsheet
from ._blacklisted_passwords import blacklisted_passwords
class Blacklist(object):
@@ -11,7 +12,7 @@ class Blacklist(object):
self.message = message
def __call__(self, form, field):
if field.data in ['password1234', 'passw0rd1234']:
if field.data in blacklisted_passwords:
raise ValidationError(self.message)

View File

@@ -5,13 +5,16 @@ from wtforms import ValidationError
from unittest.mock import Mock
def test_should_raise_validation_error_for_password(app_, mock_get_user_by_email):
@pytest.mark.parametrize('password', [
'11111111', 'kittykat', 'evangeli'
])
def test_should_raise_validation_error_for_password(app_, mock_get_user_by_email, password):
with app_.test_request_context():
form = RegisterUserForm()
form.name.data = 'test'
form.email_address.data = 'teset@example.gov.uk'
form.mobile_number.data = '441231231231'
form.password.data = 'password1234'
form.password.data = password
form.validate()
assert 'That password is blacklisted, too common' in form.errors['password']
@@ -30,7 +33,7 @@ def test_valid_email_in_valid_domains(app_):
name="test",
email_address="test@my.gov.uk",
mobile_number='4407888999111',
password='1234567890')
password='an uncommon password')
form.validate()
assert form.errors == {}

View File

@@ -117,10 +117,10 @@ def test_should_return_200_if_password_is_blacklisted(app_,
data={'name': 'Bad Mobile',
'email_address': 'bad_mobile@example.not.right',
'mobile_number': '+44123412345',
'password': 'password1234'})
'password': 'password'})
response.status_code == 200
assert 'That password is blacklisted, too common' in response.get_data(as_text=True)
assert 'Choose a password thats harder to guess' in response.get_data(as_text=True)
def test_register_with_existing_email_sends_emails(app_,

View File

@@ -284,8 +284,8 @@ def test_should_redirect_after_password_change(app_,
with app_.test_client() as client:
client.login(api_user_active)
data = {
'new_password': '1234567890',
'old_password': '4567676328'}
'new_password': 'the new password',
'old_password': 'the old password'}
response = client.post(
url_for('main.user_profile_password'),
data=data)