Check if a password has been compromised with Python and Have I Been Pwned

  

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.

πŸ”— Like Techelopment? Check out the website for all the details!

⚠️ 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:

  1. The user enters the password
  2. Python calculates the SHA-1 hash
  3. We split the hash into two parts:
    • prefix: first 5 characters
    • suffix: the rest
  4. We send only the prefix to the API
  5. The API responds with thousands of possible suffixes
  6. 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