Repository navigation
Expand file tree
/
Copy pathstrength_checker.py
More file actions
115 lines (94 loc) · 4.42 KB
/
Copy pathstrength_checker.py
File metadata and controls
115 lines (94 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
"""
Password Inspector v2.33
Copyright (c) 2025 Emmanuel Nkhoma
MIT License - See LICENSE file
"""
import re
import math
from typing import Dict, List
from zxcvbn import zxcvbn
#basic password strength checker
def checkStrength(password: str) -> Dict[str, object]:
""""
This function evaluates the strength of a password using a score system based on password best practices and entropy
Best Practices:
a. 8+ characters
b. no recurring 3 characters i.e. aaaa, 1111, e.t.c.
Entropy:
Entropy here is a measure of how guessable the password is based on multiple factors the password is matched against such as dictionary words
(English, common passwords, names, places, e.t.c), l33t substitutions of those dictionary words, repeats like aaa or xyzxyz, sequences like 1234 or abcd,
keyboard sequences like qwerty or zxcvbn and dates. The entropy score is a measure of how long or difficult it would be for a password cracker to guess a password and is calculated
by the total number of guesses each pattern would need to be cracked by an attacker assuming they took the easiest path.
** Uses zxcvbn (https://github.com/dropbox/zxcvbn)
Args*
password - string
Returns: dict
a. score - int: 0 to 100
b. issues - list[str]: too short, no numbers e.t.c.
c. strong? - boolean: True or false
"""
#Scoring criteria
score: int = 60 # Highest score without entropy check
issues: List[str] = []
#1. Length Check - Number of Characters
#1.1. Number of Characters (8+) - len()
MIN_CHAR_AMOUNT = 8
if len(password) < MIN_CHAR_AMOUNT:
score -= 10
issues.append("Too short. NIST minimum is 8 characters.")
#1.2. Number of characters (12+)
if len(password) < 12:
score -= 10
issues.append("Has less than 12 characters. Has minimum characters but can be better with more.")
#1.3. Number of characters (16+)
if len(password) < 16:
score -= 15
issues.append("Has less than 16 characters. Recommended if MFA is off.")
#1.4. Number of characters (20+)
if len(password) < 20:
score -= 15
issues.append("Has less than 20 characters. Best for password strength.")
#2. No Recurring Characters
if re.search(r'(.)\1{2,}', password):
score -= 10
issues.append("Has recurring characters (e.g aaaa, 1111): Recurring characters common in weak passwords.")
#3. Entropy Check
user_inputs = None #Contextual keywords that an attacker may have to be stricter on check
entropy_result = zxcvbn(password, user_inputs=user_inputs) #returns a dict
guesses = entropy_result['guesses'] #Minimum Number of Guesses to Crack Password
entropy_bits = math.log2(guesses) if guesses > 0 else 0
#assuming attacker attempts password cracking online with no rate-limiting
crack_time = entropy_result['crack_times_display']['online_no_throttling_10_per_second']
#Score from 0-4 based on number of guesses. [0:<10^2, 1: <10^4, 2: <10^6, 3: <10^8, 4: >= 10^10]
entropy_score = entropy_result['score']
#Add Suggestions & Warnings from zxcvbn
feedback = entropy_result.get('feedback', {}).get('suggestions', [])
for suggestion in feedback:
issues.append(f"Suggestion: {suggestion}")
warning = entropy_result.get('feedback', {}).get('warning', '')
if warning:
issues.append(f"Warning: {warning.strip()}")
added_entropy_points = 0
#Cumulative point calculation for entropy score
if entropy_score:
if entropy_score >= 0: added_entropy_points += 2
if entropy_score >= 1: added_entropy_points += 4
if entropy_score >= 2: added_entropy_points += 6
if entropy_score >= 3: added_entropy_points += 12
if entropy_score >= 4: added_entropy_points += 16
#4. Evaluate Password Strength Score & Status
final_score = score + added_entropy_points
strength = "Very Strong" if final_score >= 85 else \
"Strong" if (final_score >= 65 and final_score < 85) else \
"Fair" if (final_score >= 45 and final_score < 65) else \
"Weak" if (final_score >= 20 and final_score <= 45) else "Very Weak"
#5. Dict output
return {
"score": final_score,
"issues": issues,
"strong": strength,
"crack_time": crack_time,
"guesses": guesses,
"entropy_bits": entropy_bits,
"entropy_score": entropy_score
}