Home | Send Feedback | Share on Bluesky |

Check for Commonly Used or Compromised Passwords

Published: 3. May 2018  •  Updated: 23. October 2025  •  java, javascript

The National Institute of Standards and Technology (NIST) released a set of recommendations for handling passwords in software applications (SP 800-63-4 Digital Identity Guidelines).

The guidelines recommend that applications encourage users to create memorable passwords as long as they want (at least 8 characters when used with multi-factor authentication), using any characters. Applications should also check passwords against a list of passwords known to be commonly used, expected, or compromised and prevent users from using such passwords.

This blog post looks at a few examples that show how to implement this recommendation in a web application.

zxcvbn

zxcvbn is a JavaScript library developed and maintained by Dropbox.

zxcvbn is a password strength estimator. This library contains a list of 30,000 commonly used passwords. According to the GitHub project page, this list contains common names and surnames based on US census data, popular English words from Wikipedia, and US television and movie titles.

Add the library in npm-managed projects with

npm install zxcvbn

and then import it with

import zxcvbn from 'zxcvbn';

You can then check a password with

const result = zxcvbn(password);

The function expects one mandatory parameter: the password in plain text. The function supports a second optional parameter, an array of strings that zxcvbn treats as an extra blacklist dictionary.

const result = zxcvbn(password, ['foo', 'bar']);

The result object contains several properties about the guessability of the password. For instance, result.guesses returns the estimated number of guesses needed to crack the password. Other properties give an estimation of how long, in seconds, it would take to crack the password. Visit the project page to find a description of all the result properties.

This example focuses on the result.score property, which contains a number between 0 and 4:

This number implements a password strength meter underneath the text field. The JavaScript and CSS code in this example comes from the "Password Strength meter" post from the css-tricks blog. The blog post describes how to use a <meter> tag as a password strength meter.

const password = document.getElementById('password');
const meter = document.getElementById('password-strength-meter');
const text = document.getElementById('password-strength-text');

password.addEventListener('input', () => {
  const val = password.value;
  const result = zxcvbn(val);

  meter.value = result.score;

  if (val !== "") {
    text.innerHTML = "Strength: " + "<strong>" + strength[result.score] + "</strong>" + "<span class='feedback'>" + result.feedback.warning + " " + result.feedback.suggestions + "</span";
  }
  else {
    text.innerHTML = "";
  }
});

main.js


The zxcvbn JavaScript library has been ported to different programming languages. Check the project page to see a list of all available ports. For Java, two libraries, nbvcxz and zxcvbn4j, are available.

hibp

hibp is a JavaScript client library for the Have I been pwned? service.

Add the library in an npm-managed project with

npm install hibp

The library supports all available Have I been pwned? APIs: https://haveibeenpwned.com/API/v3

In this example, I'm only interested in the pwnedPassword method.

import { pwnedPassword } from 'hibp';

This method calls the 'search password by range' service and returns how many times a password has been exposed in a breach.

It expects the password in plain text as an argument. It runs asynchronously and returns a Promise.

    try {
      const numPwns = await pwnedPassword(password_hibp.value);
      if (numPwns > 0) {
        output.innerHTML = `Password found ${numPwns} of times in the haveibeenpwned.com database`;
      } else {
        output.innerHTML = `Password not found in the haveibeenpwned.com database`;
      }
    } catch (err) {
      output.innerHTML = err;
    }
  }

main.js

This method does not send the password in plain text to the Have I been pwned? server. Instead, it first calculates the SHA-1 hash of the plain text password locally and then sends the first 5 characters of the hash to the service. Have I been pwned? returns a list of all the hashes that start with these 5 characters. The pwnedPassword function then checks the list to see if it contains our password.

It returns either 0 if the password was not found in the Have I been pwned? database or a number greater than 0. This number represents the number of times this password was exposed in a breach.

Self-hosted Have I been pwned? database

The hibp library uses the hosted Have I been pwned? database to check the password. If sending information to an external third-party service is problematic for your application, you can host the Have I been pwned? database on your own server.

In a previous blog post, I described the process of how to download and import the Have I been pwned? password database into an embedded Xodus database.

After following the steps from that blog post, you have a local Xodus database and can write a RestController that accesses this database. Then call the endpoint from a JavaScript application.

public class SelfHostedHibp {

  private final MessageDigest md;

  private final Environment env;

  public SelfHostedHibp() throws NoSuchAlgorithmException {
    this.md = MessageDigest.getInstance("SHA-1");
    this.env = Environments.newInstance("e:/temp/pwnd");
  }

  @PreDestroy
  public void destroy() {
    if (this.env != null) {
      this.env.close();
    }
  }

  private Integer haveIBeenPwned(String password) {
    return this.env.computeInReadonlyTransaction(txn -> {
      Store store = this.env.openStore("passwords", StoreConfig.WITHOUT_DUPLICATES, txn);
      byte[] passwordBytes = this.md.digest(password.getBytes());
      ByteIterable key = new ArrayByteIterable(passwordBytes);
      ByteIterable bi = store.get(txn, key);
      if (bi != null) {
        return IntegerBinding.compressedEntryToInt(bi);
      }
      return null;
    });
  }

  @PostMapping("/selfHostedHibpCheck")
  public int selfHostedHibpCheck(@RequestBody String password) {
    Integer count = haveIBeenPwned(password);
    if (count != null) {
      return count.intValue();
    }
    return 0;
  }

}

SelfHostedHibp.java

Call the /selfHostedHibpCheck endpoint with the Fetch API in JavaScript. The service returns how many times a password has been exposed in a breach. If the number is 0, the password in question is not found in the database.

  async function checkSelfHostedHibp() {
    if (password_shhibp.value !== '') {
      try {
        const response = await fetch('/selfHostedHibpCheck', {
          body: password_shhibp.value,
          method: 'POST'
        });
        const status = await response.json();
        if (status === 0) {
          output_shhibp.innerHTML = `This password wasn't found in any of the Pwned Passwords loaded into Have I Been Pwned`;
        } else {
          output_shhibp.innerHTML = `This password has been seen ${status} times before<br>
                    This password has previously appeared in a data breach and should never be used. If you've ever used it anywhere before, change it!`;
        }

      } catch (err) {
        output_shhibp.innerHTML = err;
      }
    }
  }

main.js


You can find the complete examples on GitHub.