![]() |
When we talk about cybersecurity, one of the most common questions is:
Has my password ever been leaked in a data breach?
The Have I Been Pwned (HIBP) service (we covered it here), created by Troy Hunt, collects billions of compromised credentials from real data breaches. Fortunately, it provides an API that allows you to check a password without ever sending it in plain text (or even in its entirety).
In this article, we will build together with the reader a small Python program that uses this API securely, leveraging a technique called hash-based k-anonymity.
⚠️ Why you should never send a password
Sending a password to an external service, even if it promises not to save it, is always a bad idea. A good security API must allow checking without knowing the secret.
HIBP solves the problem with an elegant approach:
- the password is hashed locally (SHA-1)
- only the first 5 characters of the hash are sent to the server
- the server returns a list of partial hashes
- the final comparison happens on our computer
The server will never know which password we are checking.
π The technique: k-anonymity with SHA-1 hash
The flow is as follows:
- The user enters the password
- Python calculates the SHA-1 hash
- We split the hash into two parts:
- prefix: first 5 characters
- suffix: the rest
- We send only the prefix to the API
- The API responds with thousands of possible suffixes
- We verify locally if our suffix is present
In this way, our password is anonymous among thousands of others.
π§ͺ Preparing the environment
We will use only standard libraries plus requests.
pip install requests
π§ Step 1 – Calculating the SHA-1 hash of the password
import hashlib
def sha1_hash(password: str) -> str:
return hashlib.sha1(password.encode('utf-8')).hexdigest().upper()
Important note:
- the hash must be in uppercase, as required by the HIBP API
π Step 2 – Calling the Have I Been Pwned API
The endpoint to use is:
https://api.pwnedpasswords.com/range/{HASH_PREFIX}
Here is the function that makes the request:
import requests
def get_pwned_hashes(prefix: str) -> str:
url = f"https://api.pwnedpasswords.com/range/{prefix}"
response = requests.get(url)
response.raise_for_status()
return response.text
The response will be a string similar to:
0033A1D3F0A1E3C...:2
00A1B2C3D4E5F6...:154
Each line indicates how many times that password has appeared in a data breach.
π Step 3 – Checking if the password has been compromised
Now let's put it all together:
def check_password(password: str) -> int:
full_hash = sha1_hash(password)
prefix = full_hash[:5]
suffix = full_hash[5:]
hashes = get_pwned_hashes(prefix)
for line in hashes.splitlines():
hash_suffix, count = line.split(':')
if hash_suffix == suffix:
return int(count)
return 0
The function returns:
- 0 if the password has never been found
- a number > 0 if it has been compromised
π₯️ Step 4 – A simple complete program
if __name__ == "__main__":
password = input("Enter the password to check: ")
count = check_password(password)
if count:
print(f"⚠️ Password found {count} times in known data breaches!")
else:
print("✅ Password not found in Have I Been Pwned databases")
π‘ Tip: for better security, use getpass.getpass() instead of input().
π‘️ What this check does not guarantee
It's important to be honest:
- a not found password is not automatically secure
- SHA-1 is not suitable for storing passwords (but here it's just for comparison)
- a password can be weak even if it has never been breached
This tool serves to avoid already compromised passwords, not to validate their quality.
π Improvement ideas: hiding the password during input
In the example program, we used input() for simplicity, but this presents an obvious problem: the password is shown in plain text as it is typed.
In a real context, even if local, it's a bad habit because it exposes the password to:
- people looking at the screen (shoulder surfing)
- screen recordings
- accidental logs during debugging or demos
π The solution: getpass
Python provides the standard getpass module, designed specifically for this scenario. It allows reading a password from the keyboard without showing it on the screen.
Here is how to integrate it:
import getpass
password = getpass.getpass("Enter the password to check: ")
The rest of the program can remain unchanged.
✅ Advantages of getpass
- the password is not displayed
- works on Linux, macOS, and Windows
- requires no external libraries
- is the de facto standard for sensitive CLI input
⚠️ Important Note
getpass hides the input, but it doesn't make the password more secure in memory.
For more advanced applications, you might consider further measures, such as:
- clearing the variable after use
- limiting how long it remains in memory
- using isolated environments or hardware secure input
For a verification script like the one in this article, getpass still represents a clear improvement in terms of security and professionalism.
✅ Conclusion
With a few lines of Python, we have implemented:
- a real security check
- without ever transmitting the password
- using a public and reliable API
- applying an elegant cryptographic principle
It's an excellent example of how security and privacy can coexist when design is done well.
Happy ethical hacking… π
Techelopment has already thought of everything π
π The full program shown in the article, including all examples and discussed improvements, is available on GitHub, so it can be easily downloaded, run, and adapted to your needs.
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment
