Currently when configuring the password strength constraint, the "Password Strength Minimum Score" doesn't say a lot about what these scores actually mean. (Perhaps security-oriented Drupal devs know already, but I've been using Drupal for 10+ years and only have a vague idea of these and that "stronger is better" for my personal password.)

Could we consider documenting what each level corresponds to in terms of one or more of:

a) what's allowed/not allowed
b) how secure the password is when subject to brute-force attacks
c) some other metric

Ideally I think it would be helpful to have this as part of the help text when configuring that constraint, but I don't know if #2598714: Ability to alter AJAX constraints UI text in Password Policy might make that difficult. If it is, it'd be great to at least document these in the README and/or on the project page.

Comments

rootwork created an issue.

rootwork’s picture

I found the password strength code in core/modules/user/user.js.

I don't see any documentation on d.o about these password strengths. So maybe this is outside the scope of this module, although this module does surface these strength ratings more substantially, at least to site admins.

Here's the relevant section of user.js:

  /**
   * Evaluate the strength of a user's password.
   *
   * Returns the estimated strength and the relevant output message.
   *
   * @param {string} password
   *   The password to evaluate.
   * @param {object} translate
   *   An object containing the text to display for each strength level.
   *
   * @return {object}
   *   An object containing strength, message, indicatorText and indicatorClass.
   */
  Drupal.evaluatePasswordStrength = function (password, translate) {
    password = password.trim();
    var indicatorText;
    var indicatorClass;
    var weaknesses = 0;
    var strength = 100;
    var msg = [];

    var hasLowercase = /[a-z]/.test(password);
    var hasUppercase = /[A-Z]/.test(password);
    var hasNumbers = /[0-9]/.test(password);
    var hasPunctuation = /[^a-zA-Z0-9]/.test(password);

    // If there is a username edit box on the page, compare password to that,
    // otherwise use value from the database.
    var $usernameBox = $('input.username');
    var username = ($usernameBox.length > 0) ? $usernameBox.val() : translate.username;

    // Lose 5 points for every character less than 12, plus a 30 point penalty.
    if (password.length < 12) {
      msg.push(translate.tooShort);
      strength -= ((12 - password.length) * 5) + 30;
    }

    // Count weaknesses.
    if (!hasLowercase) {
      msg.push(translate.addLowerCase);
      weaknesses++;
    }
    if (!hasUppercase) {
      msg.push(translate.addUpperCase);
      weaknesses++;
    }
    if (!hasNumbers) {
      msg.push(translate.addNumbers);
      weaknesses++;
    }
    if (!hasPunctuation) {
      msg.push(translate.addPunctuation);
      weaknesses++;
    }

    // Apply penalty for each weakness (balanced against length penalty).
    switch (weaknesses) {
      case 1:
        strength -= 12.5;
        break;

      case 2:
        strength -= 25;
        break;

      case 3:
        strength -= 40;
        break;

      case 4:
        strength -= 40;
        break;
    }

    // Check if password is the same as the username.
    if (password !== '' && password.toLowerCase() === username.toLowerCase()) {
      msg.push(translate.sameAsUsername);
      // Passwords the same as username are always very weak.
      strength = 5;
    }

    // Based on the strength, work out what text should be shown by the
    // password strength meter.
    if (strength < 60) {
      indicatorText = translate.weak;
      indicatorClass = 'is-weak';
    }
    else if (strength < 70) {
      indicatorText = translate.fair;
      indicatorClass = 'is-fair';
    }
    else if (strength < 80) {
      indicatorText = translate.good;
      indicatorClass = 'is-good';
    }
    else if (strength <= 100) {
      indicatorText = translate.strong;
      indicatorClass = 'is-strong';
    }

    // Assemble the final message.
    msg = translate.hasWeaknesses + '<ul><li>' + msg.join('</li><li>') + '</li></ul>';

    return {
      strength: strength,
      message: msg,
      indicatorText: indicatorText,
      indicatorClass: indicatorClass
    };

  };
Phil Wolstenholme’s picture

@rootwork posted the Drupal core password strength code, but this module does not use that code.

This module's project page mentions that it uses https://github.com/bjeavons/zxcvbn-php which is a PHP library that estimates password strength beyond just 'does it have X number of characters, X number of uppercase characters, and X number of characters in total'.

You can see a short summary on https://github.com/bjeavons/zxcvbn-php:

zxcvbn attempts to give sound password advice through pattern matching and conservative entropy calculations. It finds 10k common passwords, common American names and surnames, common English words, and common patterns like dates, repeats (aaa), sequences (abcd), and QWERTY patterns.

And there is a longer summary on https://github.com/dropbox/zxcvbn:

zxcvbn is a password strength estimator inspired by password crackers. Through pattern matching and conservative estimation, it recognizes and weighs 30k common passwords, common names and surnames according to US census data, popular English words from Wikipedia and US television and movies, and other common patterns like dates, repeats (aaa), sequences (abcd), keyboard patterns (qwertyuiop), and l33t speak.

Consider using zxcvbn as an algorithmic alternative to password composition policy — it is more secure, flexible, and usable when sites require a minimal complexity score in place of annoying rules like "passwords must contain three of {lower, upper, numbers, symbols}".

  • More secure: policies often fail both ways, allowing weak passwords (P@ssword1) and disallowing strong passwords.
  • More flexible: zxcvbn allows many password styles to flourish so long as it detects sufficient complexity — passphrases are rated highly given enough uncommon words, keyboard patterns are ranked based on length and number of turns, and capitalization adds more complexity when it's unpredictaBle.
  • More usable: zxcvbn is designed to power simple, rule-free interfaces that give instant feedback. In addition to strength estimation, zxcvbn includes minimal, targeted verbal feedback that can help guide users towards less guessable passwords.

It's quite hard to document how the score is calculated as the library does so much! Perhaps this info may be useful, it does at least give the scores some context:

Scores are integers from 0 to 4:

0 means the password is extremely guessable (within 10^3 guesses), dictionary words like 'password' or 'mother' score a 0
1 is still very guessable (guesses < 10^6), an extra character on a dictionary word can score a 1
2 is somewhat guessable (guesses < 10^8), provides some protection from unthrottled online attacks
3 is safely unguessable (guesses < 10^10), offers moderate protection from offline slow-hash scenario
4 is very unguessable (guesses >= 10^10) and provides strong protection from offline slow-hash scenario