We’re using Django 1.3 for a new project at work. One of the things Django provides us with out of the box are convenient password change/reset forms and views. However, they don’t enforce any kind of password strength. As the application touches on financial data, it was suggested we nudge our users towards stronger passwords.
A quick search found django-passwords by Donald Stufft. The source code can be found here.
The django-passwords application provides a PasswordField and validators for enforcing stronger passwords with a number of options. I’ll show you what I did to integrate it with Django’s built-in password change and reset views.
First, install django-passwords with with pip or easy_install. Next, edit your settings.py file and add the application and your preferred password strength settings.
[python]
INSTALLED_APPS = (
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.sites’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
# Uncomment the next line to enable the admin:
‘django.contrib.admin’,
# Uncomment the next line to enable admin documentation:
# ‘django.contrib.admindocs’,
‘passwords’,
)
# Minimum password strength settings. See the GitHub page for defaults.
# https://github.com/dstufft/django-passwords/
PASSWORD_MIN_LENGTH = 8
PASSWORD_COMPLEXITY = { "UPPER": 1, "LOWER": 1, "DIGITS": 1 }
[/python]
In the example above, I’ve configured settings.py file to require passwords that are at least 8 characters long and contain at least one uppercase, one lowercase, and one numeric character. Now, any place I use a PasswordField, text entered will be validated against these requirements.
If you wish to use Django’s included password reset/change views, you have one more step do. You’ll need to extend django.contrib.auth.forms.SetPasswordForm and django.contrib.auth.forms.PasswordChangeForm and add the validate_length, complexity, dictionary_words, and common_sequences validators to those forms’ new_password1 CharFields.
EDIT: Matt Austin was kind enough to point out that all your really need to do is override one of the password fields in SetPasswordForm and PasswordChangeForm with PasswordField, so I’ve replaced my convoluted brain-dead implementation with his suggestion below.
[python]
from django.contrib.auth.forms import SetPasswordForm, PasswordChangeForm
from django.utils.translation import ugettext_lazy as _
from passwords.fields import PasswordField
class ValidatingSetPasswordForm(SetPasswordForm):
new_password2 = PasswordField(label=_("New password confirmation"))
class ValidatingPasswordChangeForm(PasswordChangeForm):
new_password2 = PasswordField(label=_("New password confirmation"))
[/python]
I put mine in a forms.py file in my application package. Finally, you need to replace the default forms with the new validating versions in the password routes of your urls.py file.
[python]
url(r’^password_change/$’, ‘django.contrib.auth.views.password_change’,
{‘password_change_form’: TimeStampedPasswordChangeForm}),
url(r’^password_changed/$’, ‘django.contrib.auth.views.password_change_done’),
url(r’^password_reset/$’, ‘django.contrib.auth.views.password_reset’),
url(r’^password_reset_done/$’, ‘django.contrib.auth.views.password_reset_done’),
url(r’^password_reset_complete/$’, ‘django.contrib.auth.views.password_reset_complete’),
url(r’^password_reset_confirm/(?P<uidb36>[-\w]+)/(?P<token>[-\w]+)/$’,
‘django.contrib.auth.views.password_reset_confirm’,
{‘set_password_form’: TimeStampedSetPasswordForm}),
[/python]
Congratulations, your site should now be enforcing the heck out of user passwords. However, let’s not get too full of ourselves. XKCD explains why this probably isn’t solving the real problem of password security anyway.