skip to content

PHP: Measuring password strength

 Tweet Share0 Tweets

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:

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:

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 to watch out for is that pwscore does not like palindromes, even if they would otherwise pass.

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; 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(($ret == 0) && preg_match("/: ([^:]+)$/", $out[0], $regs)) { list(, $msg) = $regs; switch($msg) { case "OK": if($score) { $command = "echo " . escapeshellarg($pw) . " | {$PWSCORE}"; exec($command, $out, $ret); if(($ret == 0) && 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 return values 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

Send a message to The Art of Web:


used only for us to reply, and to display your gravatar.

<- copy the digits from the image into this box

press <Esc> or click outside this box to close

Post your comment or question
top