skip to content

PHP: Measuring password strength

In our previous article on Password Validation using regular expressions and HTML5 we focused on client-side JavaScript methods for enforcing strong passwords, but to meet the latest NIST guidelines requires a dictionary check which can really only be done server-side.

NIST Guidelines

The major changes in the guidelines are:

  • to do away with what they call 'algorithmic' requirements
    (e.g. requiring that a password contains "at least one upper case letter, digit, special character");
  • to have the minimum length of at least 8 characters;
  • to not require regular password changes; and
  • to use a dictionary check.

The logic behind these changes is that the rules we've all been using up to now have failed miserably in coercing users to choose strong passwords.

In fact, passwords created using the old rules are often less secure because a large proportion of users end up settling on some variation of "Amanda1969" or "Password123" as their password.

Fortunately for us there are some easy to use open source command-line tools for checking password strength using dictionary checks and pattern recognition. They will flag passwords which are based on common words, lacking in complexity, or otherwise vulnerable to brute-force attacks.

CrackLib and libpwquality

On Debian GNU/Linux cracklib-check is available in the cracklib-runtime package, and pwscore in the libpwquality-tools package. Something equivalent should be available on other server platforms.

cracklib-check

The cracklib-check script will return either "OK" for a password that meets it's requirements, or a message indicating the problem:

# echo "Amanda1969" | cracklib-check Amanda1969: OK # echo "Password123" | cracklib-check Password123: it is based on a dictionary word

Other possible responses include:

  • it is too short;
  • it is too simplistic/systematic;
  • it does not contain enough DIFFERENT characters;
  • ...

pwscore

pwscore works in a similar fashion, but will also assign a score from 0 to 100 representing the password strength, or a reason for failure:

# echo "Amanda1969" | pwscore 53 # echo "Password123" | pwscore Password quality check failed: The password fails the dictionary check - it is based on a dictionary word

You can see from the above examples that there is some overlap between the two scripts, but in most cases they agree on whether a password is strong or weak.

One thing you should be aware of is that pwscore does not like palindromes, even if they would otherwise pass based on their complexity.

There are limited configuration options for these scripts, and you may need to find a dictionary to use.

Calling from PHP

The following password_is_vulnerable method appears in our Tools helper class as a static function:

<?PHP namespace Chirp; // Original PHP code by Chirp Internet: www.chirpinternet.eu // Please acknowledge use of this code by including this header. class Tools { public static function password_is_vulnerable($pw, $score = FALSE) { $CRACKLIB = "/path/to/cracklib-check"; $PWSCORE = "/path/to/pwscore"; // prevent UTF-8 characters being stripped by escapeshellarg setlocale(LC_ALL, 'en_US.utf-8'); $out = []; $ret = NULL; $command = "echo " . escapeshellarg($pw) . " | {$CRACKLIB}"; exec($command, $out, $ret); if((0 == $ret) && preg_match("/: ([^:]+)$/", $out[0], $regs)) { list(, $msg) = $regs; switch($msg) { case "OK": if($score) { $command = "echo " . escapeshellarg($pw) . " | {$PWSCORE}"; exec($command, $out, $ret); if((0 == $ret) && is_numeric($out[1])) { return (int) $out[1]; // return score } else { return FALSE; // probably OK, but may be too short, or a palindrome } } else { return FALSE; // OK } break; default: $msg = str_replace("dictionary word", "common word, name or pattern", $msg); return $msg; // not OK - return cracklib message } } return FALSE; // possibly OK } }

Usage:

<?PHP use \Chirp\Tools; // use just cracklib-check, return FALSE or message $retval = Tools::password_is_vulnerable($pw); // as above, but also use pwscore to measure password strength $retval = Tools::password_is_vulnerable($pw, TRUE); ?>

Example output:

Here you can see some sample output for our function both with and without the $score variable set to TRUE. Note that the final line receives no score because pwscore rejects it as a palindrome. Otherwise all passwords that satisfy cracklib-check are scored:

Password cracklib-check pwscore
Amanda1969bool(false) int(53)
ilovemypianobool(false)int(65)
jellyfi$h"it is based on a common word, name or pattern"-
jelly22fi$hbool(false)int(71)
Password123"it is based on a common word, name or pattern"-
zaq1zaq1"it does not contain enough DIFFERENT characters"-
12345678"it is too simplistic/systematic"-
use longer passwordsbool(false)int(100)
theartofwebewfotraehtbool(false)bool(false)

A cracklib-check return value of FALSE indicates that the password is acceptable.

Demonstration

In practice what we've done it wrap our PHP code into an Ajax script with it's own custom JavaScript class for calling and displaying the output in a nice format.

Here you can try it out for yourself:

Check password strength  

The UI displays either an error message, or an attractive gradient scale:

   

You can decide for yourself the minimum password length to use and whether to accept or reject low scoring passwords. Longer and higher-scoring passwords should be encouraged.

References

< PHP

User Comments

Post your comment or question

28 November, 2022

Hi,

My name is Morgan from Comparitech.
My colleague Stephen has prepared some "crib notes" on NIST's latest password guidelines.

Here's the link - comparite.ch/passwordguide

The NIST guidelines do represent best practices to implement over password security; however, important action points are buried in officious terminology and government procedural formatting which is where our guide comes in.

I hope this is helpful.

top