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.
Thanks for the tips, I’ll be using django-passwords in the project I’m currently working on.
The form seems a bit over-complicated though, could you not just override the field rather than adding code to __init__()?
MySetPasswordForm(SetPasswordForm):
new_password1 = PasswordField(label=’New password’)
That seems to do the trick?
Yeah, I can’t remember if I tried that or not. I’ll give it a shot when I get into work.
Yeah, Matt, your suggestion worked liked a charm. I don’t know what I was thinking with that convoluted mess. I’ll update the post. Thanks for the catch!
Got this error during install…
File “/../lib/python2.5/site-packages/passwords/validaors.py”, line 145
with open(dictionary) as dictionary:
^
SyntaxError: invalid syntax
For those using Python 2.5 I fixed it with this…
dictionary = open(dictionary)
try:
haystacks.extend(
[smart_unicode(x.strip()) for x in dictionary.readlines()]
)
finally:
dictionary.close()
I have used this. There may be some short comings as described in the attached link but someone has to take a real interest in your site. I overrode the registration form in django-registration and I noticed one thing. I tried to use passwords on password1 and validated that the password checked out but I could not compare it with the password2 unless it had also been changed to use Password field. It there something I am doing wrong.
What about django-password-policies? To be found at github.com/tarak/django-password-policies – disclosure: I’m Tarak, by the way. I know, self-marketing… 🙂