Sane Password Strength Validation for Django with zxcvbn

While many admins and blog posts tell users that length is by far the most important factor in creating strong passwords/passphrases, the majority of password input fields are giving them a set of hide-bound rules: Eight characters, at least one upper- and one lowercase letter, some digits and punctuation marks, etc.

Even though it includes dictionary words, a passphrase like:

Sgt. Pepper's Mr. Kite

is far stronger than:

js72(.Tb8

(there’s a world of difference between 22 characters and 9, from a cracking perspective). But many password input fields would reject the first one. No wonder users are confused by the process of creating strong passwords!

A while back, Dropbox released a very smart password validation library called zxcvbn (check the bottom left row of your keyboard) which measures strength as “entropy,” rather than as a function of adherence to a particular set of rules.

Here the overall password strength is heavily punished for including common strings, despite its length.

Because it lets users build up a strong password either by making it long or making it chaotic, I’m convinced that tools like zxcvbn are the way forward, but implementing them in pure Javascript presents a problem: To be effective, they have to do lots of dictionary lookups, and to cross-reference most-used password strings in order to penalize them. These dictionaries are too large to transmit to the browser/client quickly.

Happily, zxcvbn has been ported to work as a back-end lib. Dropbox has released a python port of zxcvbn.

Despite looking very non-random and including dictionary strings, this password gets high marks for its length and absence of commonly-used strings like “qwerty” or “abc” or common people’s names.

Running zxcvbn on the backend eliminates the dictionary size problem, but introduces a new one: You want to be able to preserve back-end field validation but still provide real-time feedback in the client. I’ve put together a kit for Django that satisfies both goals (yes there are other Django implementations for zxcvbn but none of them worked quite how I wanted).

First let’s take care of traditional/non-JS field validation, just so we’re safe. We can keep this minimal and do the fancy work on the front end later. First install the module:

pip install python-zxcvbn

And set a minimum strength threshold in settings that will apply identically to front- and back-end validation. In settings.py:

PASSWORD_MINIMUM_ENTROPY = 35

You can tweak that number to suit your purposes later. Now create a simple form object. In forms.py:

A couple of things to note there: First, I’m setting this up as a CharField rather than as a PasswordField. Second, I’m only using one password field, not two. Web forms generally use two password fields, but that’s only because password fields usually show stars, so it’s very easy for the user to mess up. We’re taking a different approach here. We want the user to experiment, and you can’t do that easily if the characters are stars. In the vast majority of situations, the user is typing in private. Let’s go with Jakob Nielsen here and make it easy for them!

Go ahead and implement that form on a test page and verify that your traditional field validation works as expected. Not super user friendly from a user feedback perspective, but it makes your form safe for the rare user (or hacker) with Javascript disabled.

We will of course provide a toggle later for users who want/need to hide the field:

Now to get fancy. To support real-time feedback, a strength indicator bar, hints and suggestions, and to disable the Submit button until our desired strength is reached, let’s create a fast/lightweight JSON endpoint to validate against. In urls.py:

url(r'^json_password_validator/$', views.json_password_validator),

And in views.py, create an endpoint that takes a password and returns some JSON that includes zxcvbn’s rich dataset explaining how it reached its conclusions, a simple valid boolean, and our site-wide threshold setting so we can access it easily in the JS:

Note that zxcvbn can also take a list of user_inputs such as their first and last name, which will be heavily penalized if used. My working version includes code to add those factors, when available.

You should now be able to hit a URL like:

http://127.0.0.1:8000/json_password_validator/?password=abc123

and get back a useful dataset. e.g.:

With that working, all we have to do is call it via jQuery as the password is being typed into the field. But we don’t want to mercilessly hammer the server. A safer approach would be to just call the endpoint when the user stops typing for a given interval. For this, I installed jquery-debounce (available through bower, if you like) and set the “stop typing” interval to 250ms. Season to taste.

… where calcScore is a function that calls the endpoint and performs a whole bunch of chocolatey interactive password strength goodness. Here’s the full Django html template code and included JS script file I used in my project. I’m using Bootstrap here – season to taste.

Post navigation

Very detailed guide. I used to work in a college of thousands of users, and password strength was always an issue. There days we try to look at multi-factor authentication, but of course, bio data has to be stored somewhere too!