The following is a guest post by Pankaj Parashar. Pankaj is our resident expert on all things <progress>
and <meter>
and this post is more evidence of that. Here, he walks us through implementing a password strength meter using what is likely the semantically best option.
A number of major websites like Dropbox, Gmail and eBay, rely on some kind of an indicator to indicate the strength of the password to the user during registration. The indicator serves as a good reminder for the user as to the level of difficulty to crack the password.
Although this practice is not new, most of the implementation for the password strength indicator I’ve seen so far uses the same old markup of <div>
and <span>
to represent the indicator. With the introduction of HTML5, we now have the ability to use the <meter>
element in our markup, which in my opinion, is semantically more accurate and perfectly suitable for this password strength indicator use-case. Throughout this article, we’ll call it the password strength meter.
<progress>
element?
Why not use the HTML5 As the name suggests, the HTML5 <progress>
element is used to indicate the progress of a task or an activity. Representing the strength of the password, isn’t really a task or activity. It’s not something that has progress towards a goal or is ever complete. Hence, it is safe to conclude that the <progress>
element is not the right candidate for this use-case.
How to calculate the strength of a password?
We’ll be using the zxcvbn library by Dropbox to calculate the strength of the password. There are quite a few alternative JavaScript libraries that computes the strength of a password, but zxcvbn is perfect for our use case because:
- It provides a simple API that takes a password as the input and returns a score from 0 to 4 as the output to indicate the strength of a password (0 – weakest, 4 – strongest). This works quite well with our
<meter>
element, that can accept avalue
within a predefinedmin
–max
range. - It estimates a realistic strength of the password, by detecting all the possible overlapping patterns and then matches it against several English dictionaries, common passwords, keyboard patterns and repetitions.
- It is built by Dropbox! The official blog provides much more information on the technical details about the algorithm.
If you are not familiar with the HTML5 <meter>
element, then CSS-Tricks has just the right article, to get you up and running with the basics. I would strongly recommend reading it before you proceed with this article.
Basic markup
Let’s start with a basic markup: an <input>
field that accepts a password, with a paired <label>
.
<label for="password">Enter password</label>
<input type="password" id="password" required>
We’ll add the <meter>
element below the <input>
along with a text element where we can affirm and explain the current value represented by the meter element. Since, zxcvbn returns a value in range of 0 to 4, the minimum possible value of the meter is 0
whereas the maximum possible value is 4
. If not specified, the default value of the min
attribute is always 0
.
<meter max="4" id="password-strength-meter"></meter>
<p id="password-strength-text"></p>
W3C recommends including a textual representation of the current value inside the meter tag for older browsers. However, we’ll keep it blank for now and discuss the possible fallback techniques later in the article.
Styling the meter
In this section, we will only focus on styling the <meter>
element. I’ll leave the styling of the rest of the markup as an exercise for you. In order to understand how to style the <meter>
element, we need to peek under the hood of the browsers to deconstruct the implementation of the <meter>
element.
For example, this is how Blink/Webkit and Gecko based browsers decompose the <meter>
tag:
The zxcvbn library returns a score from 0 to 4. This means that there are five possible values for our password strength meter. We can target each one of them using the attribute selector, eg., meter[value="0"]
, meter[value="1"]
etc.,
The score represents the strength of the password. The higher the score, the more difficult is the password to crack. We will represent each score with a different color to provide a visual feedback to the user. We can skip styling the value="0"
, as it will not be visible.
Styling the meter bar
meter {
/* Reset the default appearance */
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
margin: 0 auto 1em;
width: 100%;
height: 0.5em;
/* Applicable only to Firefox */
background: none;
background-color: rgba(0, 0, 0, 0.1);
}
meter::-webkit-meter-bar {
background: none;
background-color: rgba(0, 0, 0, 0.1);
}
Styling the meter value
/* Webkit based browsers */
meter[value="1"]::-webkit-meter-optimum-value { background: red; }
meter[value="2"]::-webkit-meter-optimum-value { background: yellow; }
meter[value="3"]::-webkit-meter-optimum-value { background: orange; }
meter[value="4"]::-webkit-meter-optimum-value { background: green; }
/* Gecko based browsers */
meter[value="1"]::-moz-meter-bar { background: red; }
meter[value="2"]::-moz-meter-bar { background: yellow; }
meter[value="3"]::-moz-meter-bar { background: orange; }
meter[value="4"]::-moz-meter-bar { background: green; }
Updating the meter
Before we proceed, lets include the zxcvbn library from cdnjs right before the body element using a <script>
tag.
<script src="https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.2.0/zxcvbn.js"></script>
zxcvbn adds a single function to the global namespace. It takes one required argument (a password) and returns a resultant object with the following properties:
- guesses – Estimated no. of guesses needed to crack the password
- guesses_log10 – Logarithmic value of guesses to the base 10
- crack_time_seconds – Estimation of the actual crack time, in seconds
- crack_time_display – Crack time in a human readable format, like, “3 hours” etc
- score – Integer in a range 0-4 (this is what we will be using for the meter)
- feedback.warning – Explains what’s wrong with the entered password
- feedback.suggestions – List of suggestions to help choose a less guessable password
- sequence – List of patterns that zxcvbn based the guess calculation on
- calc_time – The time it took the zxcvbn to calculate an answer, in milliseconds
zxcvbn also includes an optional user_inputs
argument which accepts an array of strings that it uses as a blacklist to penalize passwords containing user’s personal information from other fields like name, email etc.
Now every time the password field is changed, we will pass the password to the zxcvbn function and update the meter using the resultant score
. In addition to updating the value
attribute of the meter, we will also update the accompanying text to indicate the strength of the password to the user.
Firstly, we will map the scores to a human readable format,
var strength = {
0: "Worst",
1: "Bad",
2: "Weak",
3: "Good",
4: "Strong"
}
Secondly, attach a listener to the password field that will listen for the changes and then update the meter and the text indicator.
var password = document.getElementById('password');
var meter = document.getElementById('password-strength-meter');
var text = document.getElementById('password-strength-text');
password.addEventListener('input', function() {
var val = password.value;
var result = zxcvbn(val);
// Update the password strength meter
meter.value = result.score;
// Update the text indicator
if (val !== "") {
text.innerHTML = "Strength: " + strength[result.score];
} else {
text.innerHTML = "";
}
});
The demo Pen additionally employs feedback.warnings
and feedback.suggestions
to provide a relevant and useful feedback to the user, to help them choose a less-guessable password.
See the Pen Password strength meter by Pankaj Parashar (@pankajparashar) on CodePen.
If for any reason the demo fails in your browser, you can watch this video.
Fallback
As it stands, our password strength meter works quite well in all the browsers that support the HTML5 <meter>
element. The good thing is, we do not have to worry about the browsers that don’t support it. Those browsers will simply ignore the <meter>
tag and render the text after the meter, which is a decent fallback to indicate the strength of the password to the user.
If you’re determined to provide a consistent user experience across all the browsers, you could simulate the appearance of the meter using a combination of <div>
and <span>
markup inside the <meter>
element. Browsers that do not understand the <meter>
tag will simply ignore it and instead render the markup inside. I’ve described this method in detail in the fallback section of my previous article on CSS-Tricks on the same topic.
Are password strength meters good from the UX point of view?
This article doesn’t intend to spark a debate on whether password strength meters are good or not. There are probably, rational and reasonable arguments on both the sides. However, most of the argument stems from the inability of the algorithm to provide a realistic measure of the strength of the password. I think Dropbox has nailed it with the zxcvbn library because it offers a much more realistic estimation of how difficult is the password to crack.
Whether you use it in your design or not, is up to you. But if you decide to take the plunge, then make sure you use the HTML5 <meter>
element!
If you’re going for a hue shift for the meter indicator, I’d flip orange and yellow (red > orange > yellow > green).
Yep. That’s part of the natural order of the color spectrum: red > orange > yellow > green > blue > indigo > violet. I’ve always used the following mnemonic to remember the order: Robert of York gained battles in vain. Although, I just discovered upon doing a search on Google that it’s actually Richard of York who gained the battles. The thing is, I don’t even know who Robert or Richard is and what battles were fought!
Makes sense. Thanks @Kevin and @Wayne for the input. I’ll keep that in mind going forward.
@Wayne the mnemonic is actually Richard Of York Gave Battle In Vain. It refers to Richard III of England (who was of the house of York) and his defeat by the Lancastrian forces, led by Henry Tudor (who became Henry VII of England) at the Battle of Bosworth in 1485
Am I understanding correctly that passwords are being sent to another site (api)? In the clear? Is this not bad? (Genuinely asking).
No, the zxcvbn api is loaded on the client. Nothing is being sent until the user submits the form, at which point the information is (hopefully) being sent to your server.
This is not correct. The source code for the library has a massive list of strings that are common for passwords. It’s matching against those, and general patterns, to get the result. No network activity. No plain text passwords transmitted.
I think the api is imported as JS file into the implementation from
https://cdnjs.cloudflare.com/ajax/libs/zxcvbn/4.2.0/zxcvbn.js
so there is sending of any password to any other site.
You could make things just that little more ‘nice’ by adding a transition to the bar as it fills up:
Also, I would style the 0 value if you were doing this because when you remove the password the meter flashes to green (the default) when transitioning back to 0.
http://codepen.io/fatmedia/pen/PZYrBr – here’s a Pen to show it :-)
zxcvbn.js and the javascript should be put just before because they need access to the completed DOM.
Ha I actually typed in ‘aaa’…