Created
May 27, 2023 18:39
-
-
Save hartwork/4ba09a83e66c0f5e7937e7351fe0039d to your computer and use it in GitHub Desktop.
Test a password against the Have-I-Been-Pwned database API interactively with ease and k-anonymity (Python 3)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# Copyright (C) 2023 Sebastian Pipping <[email protected]> | |
# Licensed under the MIT license | |
# | |
# Version 2023-05-27 20:38 UTC+2 | |
# | |
# Inspired by https://github.com/weddige/django-hibp/blob/main/django_hibp.py | |
# of django-hibp by Konstantin Weddige (@weddige). | |
import getpass | |
import hashlib | |
import signal | |
import sys | |
import urllib.request | |
def test_password(password: str) -> [str, int]: | |
full_upper_sha1 = hashlib.sha1(password.encode("utf-8")).hexdigest().upper() | |
upper_sha1_prefix = full_upper_sha1[:5] | |
url = f"https://api.pwnedpasswords.com/range/{upper_sha1_prefix}" | |
response = urllib.request.urlopen(url).read().decode("utf-8") | |
upper_sha1_remainder = full_upper_sha1[5:] | |
for line in response.split("\n"): | |
if line.startswith(upper_sha1_remainder): | |
_, times, *_ = line.split(":") | |
return upper_sha1_prefix, int(times) | |
return upper_sha1_prefix, 0 | |
def main(): | |
print( | |
"WARNING: The first and last characters of your password will be echoed back to the terminal." | |
) | |
password = getpass.getpass(prompt="Password: ") | |
upper_sha1_prefix, seen_times = test_password(password) | |
if seen_times == 0: | |
exit_code = 0 | |
summary = "not seen, good (no guarantee!)" | |
else: | |
exit_code = 1 | |
summary = f"seen {seen_times:,} times, BAD" | |
obfuscated_password = f"{password[0]}[..]{password[-1]}" | |
obfuscated_sha1 = f"{upper_sha1_prefix}[..]" | |
print(f"Password {obfuscated_password!r} (SHA1 {obfuscated_sha1}) {summary}.") | |
return exit_code | |
if __name__ == "__main__": | |
try: | |
exit_code = main() | |
except KeyboardInterrupt: | |
exit_code = 128 + signal.SIGINT | |
sys.exit(exit_code) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment