SiriusStarr/elm-password-strength/1.0.1

NOTE: the latest version is 1.0.2

ZxcvbnPlus

PasswordStrength.ZxcvbnPlus is a native Elm implementation of the popular zxcvbn password strength javascript library. If you require exactly identical results to the reference implementation, please use PasswordStrength.Zxcvbn instead. More information may be found on its GitHub. The following is excerpted from the README:

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.

For further detail and motivation, please refer to the USENIX Security '16 paper and presentation.

Usage Notes

The results returned by this package differ somewhat from the reference (javascript) implementation. If you require exactly identical results, consider using Zxcvbn instead.

The computational performance of this library is quite good, with essentially instantaneous results for normal length passwords and barely perceptible delays even with ~100-character passwords.

It should be noted that the zxcvbn library is not "light" by any means, given the large dictionaries of common words it requires. Sizes for an extremely simple example application follow:

  • Compiled size -- 3.69 MB
  • Minified size -- 1.85 MB
  • Gzipped size -- 694 KB

Differences from Zxcvbn

In general, this package differs from the reference implementation in being more "Elm"-y. For example, custom types are used in place of strings in return values, regexes are avoided in favor of parsers, etc. Unlike the reference implementation, it also will not erroneously flag invalid dates, e.g. February 31st.

Known Issues

With exceedingly difficult to crack passwords, the algorithm may report bruteforce matches as "more guessable" than actual matches in the password. This is because the number of possible guesses for bruteforce caps at approximately 10^308 guesses. Since this is truly absurdly large, it should never be problematic in normal use.

Functions

This package exposes exactly one function, zxcvbnPlus, which requires no configuration of any kind and may be simply called with a password (and possibly-empty list of additional patterns).

zxcvbnPlus : List String -> String -> ZxcvbnPlusResult

zxcvbnPlus takes two arguments, a List String of user patterns and a String password, and returns a result record of type ZxcvbnResult.

The user patterns argument (which may be an empty list) is an array of strings that zxcvbnPlus will treat as an extra dictionary. This can be whatever list of strings you like but is meant for user inputs from other fields of the form, like name and email. That way a password that includes a user's personal information can be heavily penalized. This list is also good for site-specific vocabulary -- Acme Brick Co. might want to include ["acme", "brick", "acmebrick", etc]. This list is ordered in increasing rank, which the first element of the list being the highest rank (most common). Entries with higher ranks are more strongly penalized. However, unless a truly massive list is used, the ordering is unlikely to be relevant. User patterns are not case-sensitive.

Result Types

The sole function in this package returns one relatively complex record containing the full results of the password strength check.

type alias ZxcvbnPlusResult =
{ score : Score
, feedback : Feedback
, crackTimesDisplay : DisplayCrackTimes
, crackTimesSeconds : NumericCrackTimes
, guesses : Int
, guessesLog10 : Float
, password : String
, matchSequence : List ScoredMatch
}

ZxcvbnPlusResult is returned by zxcvbnPlus and holds the results of the password strength check. It has the following fields:

  • score -- The overall score of the password (useful for displaying to the user, e.g. as a strength bar).
  • feedback -- A record containing user-friendly feedback to help choose better passwords; is empty if the score is sufficiently high.
  • crackTimesDisplay -- A record with user-friendly display strings for the crack time estimations, e.g. "less than a second", "3 hours", "centuries", etc.
  • crackTimesSeconds -- A record containing back-of-the-envelope crack time estimations, in seconds, based on a few scenarios.
  • guesses -- The estimated guesses needed to crack the password.
  • guessesLog10 -- The order of magnitude of the estimated number of guesses to crack the password.
  • password -- The password that was checked.
  • sequence -- The list of patterns that zxcvbn based the guess calculation on. Rather complicated and typically unnecessary, but the contained types are exposed in ZxcvbnPlus.MatchTypes if they are needed.
type Score
= TooGuessable
| VeryGuessable
| SomewhatGuessable
| SafelyUnguessable
| VeryUnguessable

The overall score for a password's quality, e.g. for display to the user as a growing bar.

  • TooGuessable -- Risky password: "too guessable"
  • VeryGuessable -- Modest protection from throttled online attacks: "very guessable"
  • SomewhatGuessable -- Modest protection from unthrottled online attacks: "somewhat guessable"
  • SafellyUnguessable -- Modest protection from offline attacks: "safely unguessable" assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2, argon, etc
  • VeryUnguessable -- Strong protection from offline attacks under same scenario: "very unguessable"
type alias Feedback =
{ warning : Maybe String
, suggestions : List String
}

Contains feedback inteded to be displayed to the user trying to create a password.

  • warning: Explains what's wrong, e.g. "This is a top-10 common password". Will be Nothing if no applicable warnings are found.
  • suggestion: A possibly-empty list of suggestions to help choose a less guessable password, e.g. "Add another word or two".
type alias DisplayCrackTimes =
{ onlineThrottling100PerHour : String
, onlineNoThrottling10PerSecond : String
, offlineSlowHashing1e4PerSecond : String
, offlineFastHashing1e10PerSecond : String
}

Crack times for password in user-friendly format (e.g. "less than a second", "10 years", etc.) for various cases.

  • onlineThrottling100PerHour -- Online attack on a service that ratelimits password auth attempts.
  • onlineNoThrottling10PerSecond -- Online attack on a service that doesn't ratelimit, or where an attacker has outsmarted ratelimiting.
  • offlineSlowHashing1e4PerSecond -- Offline attack. assumes multiple attackers, proper user-unique salting, and a slow hash function w/ moderate work factor, such as bcrypt, scrypt, PBKDF2.
  • offlineFastHashing1e10PerSecond -- Offline attack with user-unique salting but a fast hash function like SHA-1, SHA-256 or MD5. A wide range of reasonable numbers anywhere from one billion - one trillion guesses per second, depending on number of cores and machines. Ballparking at 10B/sec.
type alias NumericCrackTimes =
{ onlineThrottling100PerHour : Float
, onlineNoThrottling10PerSecond : Float
, offlineSlowHashing1e4PerSecond : Float
, offlineFastHashing1e10PerSecond : Float
}

Crack times for password in seconds for various cases.

  • onlineThrottling100PerHour -- Online attack on a service that ratelimits password auth attempts.
  • onlineNoThrottling10PerSecond -- Online attack on a service that doesn't ratelimit, or where an attacker has outsmarted ratelimiting.
  • offlineSlowHashing1e4PerSecond -- Offline attack. assumes multiple attackers, proper user-unique salting, and a slow hash function w/ moderate work factor, such as bcrypt, scrypt, PBKDF2.
  • offlineFastHashing1e10PerSecond -- Offline attack with user-unique salting but a fast hash function like SHA-1, SHA-256 or MD5. A wide range of reasonable numbers anywhere from one billion - one trillion guesses per second, depending on number of cores and machines. Ballparking at 10B/sec.