Here's the dirty secret of password security: those little bars that turn green when you add an exclamation mark or a number are basically fancy lies. You know the drill"Password must be 8+ characters and include uppercase letters, numbers, and special symbols. "It sounds scientific. It sounds secure. It's not.
Let me show you why. Take "P@ssw0rd123" it looks strong, right? It's got everything: uppercase, lowercase, numbers, symbols. All the boxes ticked. The checker probably gives it a "Very Strong" rating. Here's the thing: this password is garbage. It's in breach databases millions of times. Attackers don't care that you added an exclamation mark when they already have your password from a data breach. They're not sitting there guessing character by character—they're pulling up lists of hundreds of millions of real, exposed passwords and trying them.
This is the fundamental problem with conventional password checkers. They reward superficial complexity without checking whether your password has already been compromised. It's like checking if your front door has a fancy lock while ignoring the fact that someone already stole the key.
And here's what makes it worse: users aren't stupid. When you force them to add symbols and numbers, they predictably do the same things: "Password1!" "Welcome 2024!" "P@ssw0rd123!" These patterns are the absolute first entries in any attacker's word list. The National Institute of Standards and Technology (NIST) actually reversed its support for these complexity rules in 2017 precisely because they don't work. Most systems aren't checking their database. Most aren't querying breach APIs. They're just counting characters and calling it a day. The Verizon Data Breach Investigations Report found that over 81% of hacking-related breaches exploit either stolen or weak passwords. We're losing the battle.
So I spent my final year building something better. A password strength checker that doesn't just look at your password—it actually checks if anyone's already stolen it.
The tech stack: a Flask backend, a Random Forest machine learning classifier, and integration with the Have I Been Pwned (HIBP) API—the internet's biggest database of breached passwords. The system evaluates passwords through four layers: entropy analysis (how random does this look?), machine learning classification (trained on real breach data), real-time breach checking (is this in HIBP?), and cryptographic storage (bcrypt hashing so plaintext passwords never touch the disk).
Here's the core evaluation route in action:
@app.route('/api/evaluate', methods=['POST'])
def evaluate_password():
password = request.json.get('password')
Step 1: Check if breached (absolute disqualifier)
sha1_hash = hashlib.sha1(password.encode()).hexdigest().upper()
prefix = sha1_hash[:5]
response = requests.get(f'https://api.pwnedpasswords.com/range/{prefix}')
if sha1_hash[5:] in response.text:
return {'verdict': 'COMPROMISED', 'breach_count': ...}
Step 2: ML classifier and entropy analysis
features = extract_features(password)
ml_rating = model.predict([features])[0]
entropy_score = calculate_shannon_entropy(password)
return {
'verdict': ml_rating,
'entropy': entropy_score,
'ml_rating': ml_rating
}
The Random Forest classifier was trained on 150 passwords structured across six character-composition categories—numeric, alphabetic, mixed-case, alphanumeric, alpha-special, and full diversity. It learned that character-class diversity matters more than length alone. A 15-character numeric password is still weak. A 10-character password with uppercase, lowercase, numbers, and symbols, that's strong. The model achieved 93.3% accuracy in cross-validation. But more importantly, it learned the right patterns. Feature importance analysis showed the classifier bases its decisions on security-relevant properties: entropy, special character ratios, bigram entropy, and repeated character detection, not just "Is this password long enough?"
Here's the moment that changed everything for my project, and it's the reason this blog post exists. I tested a password called "Tr0ub4dor&3." On paper, it looks fantastic. It's got uppercase, lowercase, numbers, and symbols. My machine learning classifier correctly rated it as "Strong" based on structural complexity alone. But here's the problem: this password has appeared in 3,196 known data breaches. Attackers already have it. They've got it on their wordlists. It's a known, compromised credential.
My system caught it. The breach detection module saw the HIBP match and did something most checkers won't: it overrode everything. That "Strong" rating from the ML classifier didn't matter. The entropy score didn't matter. The system said, "You think this is strong? It's in 3,196 breaches. It's trash."
Look at this comparison. The password 123456 appeared in 210,318,957 breaches and was rated Very Weak. Password1! appeared in 584,516 breaches and was rated Weak. Tr0ub4dor&3 appeared in 3,196 breaches but was rated Strong from the ML classifier, yet the final verdict was Very Weak. Meanwhile, kX9#mP2$vL5@nQ8! has never been breached and rated Very Strong.
Look at that third example again. A "Strong" password from the ML classifier got rejected as "Very Weak." Why? Because breach status is empirical reality, not a probability. A password can score perfectly on every metric and still be compromised. This is the gap every traditional checker misses.
This was my "wait, what?" moment. I realized that checking breach status isn't just another feature; it's the most important feature. It provides ground-truth information that no structural evaluation can substitute. NIST SP 800-63B actually requires this check. Most systems don't bother implementing it.
Let me walk you through what happens when someone enters a password into my system. Imagine you type "kX9#mP2$vL5@nQ8!" into the registration form.
Step 1: Entropy Analysis. The system calculates Shannon entropy, a mathematical measure of unpredictability. Your password scores 4.0 out of 4.0 ("Very High"). Good start.
Step 2: Feature Extraction. The system builds a 28-dimensional feature vector. Length? Check. Character class distribution? Check. Bigram entropy? Check. Keyboard walk detection? Check. Date pattern detection? Check. Common word proximity? Check. It's capturing everything that makes a password predictable, or not.
Step 3: Machine Learning Classification. The Random Forest model takes those 28 features and returns a rating: "Very Strong." All four character classes are present, good length, high entropy, and have no predictable patterns. The classifier is satisfied.
Step 4: Breach Detection. Here's where most systems stop. Mine doesn't. The system computes the SHA-1 hash of your password, takes the first five characters (the k-anonymity prefix), and queries HIBP. The API returns all breached hashes with that prefix—usually 100 to 1,000 entries. The system checks locally whether your full hash appears.
Result for kX9#mP2$vL5@nQ8!: Not breached. Breach count equals 0.
Step 5: Final Decision. All checks passed. The verdict is "very strong," and the password is accepted. It's hashed with bcrypt at cost factor 12 (about 250 milliseconds of hashing work), salted with a unique random value, and stored securely.
Now imagine you type "Tr0ub4dor&3" instead. The workflow is the same—until Step 4. The HIBP query comes back with a match. Breach count equals 3,196. The system doesn't care that the ML classifier said "Strong." It doesn't care about the entropy score. It applies the absolute disqualifier rule: if breached, the verdict is "Very Weak." It's that simple. And it's that important.
Alright, here's the reality check. If you're reading this and thinking, "Okay, how do I protect myself?"—here's what actually matters.
First, use a password manager. This is non-negotiable. Generate random, long passwords for every single account. Don't try to remember them. That's what password managers are for.
Second, check HIBP yourself. Go to haveibeenpwned.com. Check your email address. Check your passwords. If something shows up as breached, change it immediately, even if it "looks" strong.
Third, understand that length matters, but breach status matters more. A 20-character password that's never been breached is better than a 12-character password with all the symbols that's in 3,000 breaches. The breach check is the real security.
And don't trust the green bar. Those password strength meters you see on most websites? They're counting characters, not checking compromises. The green bar means "this satisfies our rules," not "this is actually secure."
I spent my final year building a password checker that actually works and the biggest lesson wasn't about machine learning or cryptography. It was this: security isn't about how complex your password looks. It's about whether attackers already have it. Most systems are still checking the wrong things. They reward you for adding an exclamation mark while ignoring the fact that your password was leaked in the LinkedIn breach. That's not security. That's theater.
The system I built is open, documented, and evaluated. It's not just theory—it's a working Flask application with real-time feedback, breach detection, and cryptographic storage. And the key lesson from the whole project? Always check the breach databases. Always. Because "P@ssw0rd123!" might be complex, but it's been stolen a million times. And your password? If it's in a breach, it doesn't matter how many symbols you added. It's already compromised.
Built with Python, Flask, scikit-learn, and the Have I Been Pwned API.
Top comments (0)