Skip to content

Instantly share code, notes, and snippets.

@nfitzen
Last active January 21, 2022 16:59
Show Gist options
  • Save nfitzen/f06b41108e72f9c346bca23c13a97a4c to your computer and use it in GitHub Desktop.
Save nfitzen/f06b41108e72f9c346bca23c13a97a4c to your computer and use it in GitHub Desktop.
Looks up passwords on Have I Been Pwned.
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# MIT License
#
# Copyright (C) 2021 Nathaniel Fitzenrider <https://github.com/nfitzen>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice (including the
# next paragraph) shall be included in all copies or substantial portions
# of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
# Looks up passwords on Have I Been Pwned's passwords service.
# This way, I have a somewhat auditable program for checking whether a
# password was breached. It's certainly easier to inspect than a webpage's JS.
# See <https://haveibeenpwned.com/Passwords>.
# Note: might not be safe, since I didn't check the security
# with respect to, e.g., memory or terminal usage.
import httpx
from hashlib import sha1
from getpass import getpass
from typing import Any, Optional
def find_password(data: str, suffix: str) -> Optional[tuple[str, int]]:
if not data:
return
suffix = suffix.upper()
for line in data.split("\n"):
parsed: Any = line.split(':')
parsed[1] = int(parsed[1])
parsed = tuple(parsed)
if parsed[0] == suffix and parsed[1]:
return (parsed[0], int(parsed[1]))
def hibp_lookup(pwd: str) -> Optional[tuple[str, int]]:
pwd_hash = sha1(bytes(pwd, 'utf-8')).hexdigest().upper()
prefix = pwd_hash[:5]
suffix = pwd_hash[5:]
url = "https://api.pwnedpasswords.com/range/"
headers = {"Add-Padding": "true"}
request = httpx.get(url + prefix, headers=headers)
data = request.text
return find_password(data, suffix)
def main():
while True:
try:
pwd = getpass()
except KeyboardInterrupt:
print("\nExiting...")
quit()
if pwd == '':
print("Exiting...")
quit()
lookup = hibp_lookup(pwd)
if lookup is not None:
number = lookup[1]
print("PASSWORD FOUND. Make sure to take the proper precautions!")
if number > 1:
print(f"It was found {number} times.")
else:
print(f"It was found 1 time.")
else:
print("Password wasn't found. Doesn't mean your password was necessarily secure.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment