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
MacOS:
pip3 install zxcvbn
Linux:
pip install zxcvbn
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)
We get the following password:
2Ub!{V%kr=B(qhI
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)
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': []}}
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'},
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)
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.']}}
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"])
Output:
{'warning': 'This is a top-10 common password.', 'suggestions': ['Add another word or two. Uncommon words are better.']}
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)