DEV Community

Cover image for Checking password strength using the zxcvbn library
Magnifique numérique
Magnifique numérique

Posted on

Checking password strength using the zxcvbn library

Greetings to everyone! In this article, we will check password security using the zxcvbn library, which determines password strength and helps improve it.

Installing the library

Windows:

pip install zxcvbn
Enter fullscreen mode Exit fullscreen mode

MacOS:

pip3 install zxcvbn
Enter fullscreen mode Exit fullscreen mode

Linux:

pip install zxcvbn
Enter fullscreen mode Exit fullscreen mode

Checking a Strong Password

I suggest generating a random password using the module we developed in an earlier article. Thus, here is the code to generate a random password of 15 characters of any type (letters, numbers, special characters):

manager = Password()
password = manager.generate(15, "all")

print(password)
Enter fullscreen mode Exit fullscreen mode

We get the following password:

2Ub!{V%kr=B(qhI
Enter fullscreen mode Exit fullscreen mode

Checking password strength

So, to obtain all data regarding password strength, the zxcvbn() function is used, which provides all information about our password. Now, let's pass our password to the zxcvbn() function and print the result:

protection_report = zxcvbn.zxcvbn("2Ub!{V%kr=B(qhI")
print(protection_report)
Enter fullscreen mode Exit fullscreen mode

Output:

{'password': '2Ub!{V%kr=B(qhI', 'guesses': Decimal('1000000000000001'), 'guesses_log10': 14.999999999999998, 'sequence': [{'pattern': 'bruteforce', 'token': '2Ub!{V%kr=B(qhI', 'i': 0, 'j': 14, 'guesses': 1000000000000000, 'guesses_log10': 14.999999999999998}], 'calc_time': datetime.timedelta(microseconds=2999), 'crack_times_seconds': {'online_throttling_100_per_hour': Decimal('36000000000000037.99840144433'), 'online_no_throttling_10_per_second': Decimal('100000000000000.1'), 'offline_slow_hashing_1e4_per_second': Decimal('100000000000.0001'), 'offline_fast_hashing_1e10_per_second': Decimal('100000.0000000001')}, 'crack_times_display': {'online_throttling_100_per_hour': 'centuries', 'online_no_throttling_10_per_second': 'centuries', 'offline_slow_hashing_1e4_per_second': 'centuries', 'offline_fast_hashing_1e10_per_second': '1 day'}, 'score': 4, 'feedback': {'warning': '', 'suggestions': []}}

Enter fullscreen mode Exit fullscreen mode

As a result, we received a dictionary with truly rich content, which shows us various aspects of password strength. Below, I have defined each key for a deeper understanding.

About the ‘password’ key

The password we passed to the function.

About the ‘guesses’ key

The number of attempts required to guess the password.

About the ‘guesses_log10’ key

Converts the number of guesses in the ‘guesses’ key into a more convenient format from 1 to 15.

About the ‘pattern’ key

This is sort of the password template type. In this case, it says ‘bruteforce’ because our password is a set of random characters.

About the ‘token’ key

This is the part of our password that matches the pattern.

About the ‘i’ and ‘j’ keys

During the password check, the program verifies the password by determining its pattern by separate parts precisely thanks to these indices. Key ‘i’ is the start, and ‘j’ is the end.
For example, if our password contains a name and a birth year, the program will understand via these indices that the first part is a name (by pattern), and the second is a birth year (this will be a different pattern).

About the ‘calc_time’ key

The time it took our processor to execute the zxcvbn() function.

About the ‘score’ key

Password strength score. The password is rated from 0 to 4. That is, in this case, our password is maximally secure.

About the ‘feedback’ key

Here we can see warnings regarding password strength (‘warning’) and options for improving it (‘suggestions’).

Separately about the keys crack_times_seconds and crack_times_display

I also consider it necessary to describe these keys in a separate part of the article:

'crack_times_seconds': {'online_throttling_100_per_hour': Decimal('36000000000000037.99840144433'), 'online_no_throttling_10_per_second': Decimal('100000000000000.1'), 'offline_slow_hashing_1e4_per_second': Decimal('100000000000.0001'), 'offline_fast_hashing_1e10_per_second': Decimal('100000.0000000001')}, 'crack_times_display': {'online_throttling_100_per_hour': 'centuries', 'online_no_throttling_10_per_second': 'centuries', 'offline_slow_hashing_1e4_per_second': 'centuries', 'offline_fast_hashing_1e10_per_second': '1 day'},
Enter fullscreen mode Exit fullscreen mode

Here, possible attack types for cracking the password are shown, along with the time a fraudster would spend on each of these types. The key 'crack_times_seconds' shows this time in seconds, and 'crack_times_display' shows it in a format more familiar to us: days, months, years, etc.
I suggest considering the context of each of these types for better understanding.

The first type ('online_throttling_100_per_hour') is a situation where a fraudster tries to guess your password via a site (meaning online), with their limit being 100 attempts per hour. The fraudster visits the site just like all other users. This type has limitations because, after several failed attempts, the site will start enabling bot protection.

The second type ('online_no_throttling_10_per_second') is a situation where a fraudster also guesses the password, but with a limit of 10 attempts per second. Here the fraudster sends requests directly to the server, so there are fewer restrictions.

The third type ('offline_slow_hashing_1e4_per_second') is a situation where the fraudster has a database of passwords (meaning this attack is no longer online), but the passwords are protected by a reliable algorithm.

The fourth type ('offline_fast_hashing_1e10_per_second') is a situation where the fraudster has a database of passwords, but they are protected by an unreliable algorithm. And obviously, the password will be easily cracked then, even if it is generally reliable.

Checking the security of a weak password

Now let’s try applying the zxcvbn() function to a weak password to see the conclusion about it:

protection_report = zxcvbn.zxcvbn("qwerty")
print(protection_report)
Enter fullscreen mode Exit fullscreen mode

Here is the information we received about it:

{'password': 'qwerty', 'guesses': Decimal('5'), 'guesses_log10': 0.6989700043360187, 'sequence': [{'pattern': 'dictionary', 'i': 0, 'j': 5, 'token': 'qwerty', 'matched_word': 'qwerty', 'rank': 4, 'dictionary_name': 'passwords', 'reversed': False, 'l33t': False, 'base_guesses': 4, 'uppercase_variations': 1, 'l33t_variations': 1, 'guesses': 4, 'guesses_log10': 0.6020599913279623}], 'calc_time': datetime.timedelta(microseconds=109428), 'crack_times_seconds': {'online_throttling_100_per_hour': Decimal('180.0000000000000099920072216'), 'online_no_throttling_10_per_second': Decimal('0.5'), 'offline_slow_hashing_1e4_per_second': Decimal('0.0005'), 'offline_fast_hashing_1e10_per_second': Decimal('5E-10')}, 'crack_times_display': {'online_throttling_100_per_hour': '3 minutes', 'online_no_throttling_10_per_second': 'less than a second', 'offline_slow_hashing_1e4_per_second': 'less than a second', 'offline_fast_hashing_1e10_per_second': 'less than a second'}, 'score': 0, 'feedback': {'warning': 'This is a top-10 common password.', 'suggestions': ['Add another word or two. Uncommon words are better.']}}
Enter fullscreen mode Exit fullscreen mode

As we can see, all aspects of the password clearly give us the understanding that it is unreliable. Furthermore, we can see some new keys in the dictionary, which also have their own meaning.
The meaning of the new keys can be seen in the table below.

About the ‘matched_word’ key

This is the word from the dictionary that matches our password.

About the ‘rank’ key

The place this password occupies in the list of popular passwords.

About the ‘dictionary_name’ key

Shows the name of the dictionary where the password was directly found.

About the ‘reversed’ key

Checks if the password is written backwards.

About the ‘l33t’ key

Checks if there are no letter-to-number or symbol substitutions in the password. This can often be seen in various passwords.
For example, instead of password, it might be written as pa$$word.

About the ‘base_guesses’ key

The base number of attempts required to guess the password if it contained no changes.

About the ‘uppercase_variations’ key

Determines the complexity level of the password if it contains uppercase letters.

About the ‘l33t_variations’ key

Determines the complexity level of the password if it replaced letters with numbers or symbols.

It is also worth remembering that we can simply select specific values via keys, rather than outputting all information about the password completely. For example, here we output recommendations for improving the password:

protection_report = zxcvbn.zxcvbn("qwerty")
print(protection_report["feedback"])
Enter fullscreen mode Exit fullscreen mode

Output:

{'warning': 'This is a top-10 common password.', 'suggestions': ['Add another word or two. Uncommon words are better.']}
Enter fullscreen mode Exit fullscreen mode

Conclusion

So, in this article, we reviewed the zxcvbn() library, which allows for fully checking passwords for their reliability thanks to a wide spectrum of possible check types. We also compared the conclusions for a strong and a weak password. Thank you all for your attention!

Top comments (0)