Password Strength `meter`

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.

Showcasing the password strength indicators from eBay(Top), Gmail(Middle) and Dropbox(Bottom) in varying forms, essentially representing the same information.

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.

Why not use the HTML5 <progress> element?

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:

  1. 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 a value within a predefined min-max range.
  2. 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.
  3. 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:

Illustrating the implementation level detail of the HTML5 <meter> element in the various rendering engines.

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!