Skip to content

Instantly share code, notes, and snippets.

@ianloic
Created November 26, 2016 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ianloic/b32db4dfd8d03a3c0ffbb4d1eca9be6a to your computer and use it in GitHub Desktop.
Save ianloic/b32db4dfd8d03a3c0ffbb4d1eca9be6a to your computer and use it in GitHub Desktop.
Password Reuse Checker
#!/usr/bin/env python3
import argparse
import sys
import datetime
import secretstorage
import functools
def datetime_from_ss(ts):
datetime.datetime.fromtimestamp(int(ts) / 10000000)
@functools.total_ordering
class StoredPassword:
def __init__(self, item):
self.item = item
self.url = item.get_label()
self.password = item.get_secret().decode()
attributes = item.get_attributes()
self.realm = attributes['signon_realm']
self.username = attributes['username_value']
self.created = datetime_from_ss(attributes['date_created'])
self.synced = datetime_from_ss(attributes['date_synced'])
def __id__(self):
return id(self.item)
def __eq__(self, other):
return self.item == other.item
def __hash__(self):
return hash(self.item)
def __lt__(self, other):
return (self.created, self.synced) < (other.created, other.synced)
def __repr__(self):
date_format = '%Y-%m-%d'
return 'StoredPassword(%s %s %s %s %s)' % (
self.realm, self.username, self.password,
self.created.strftime(date_format),
self.synced.strftime(date_format))
def get_all_passwords():
print('Loading passwords...')
bus = secretstorage.dbus_init()
collection = secretstorage.get_default_collection(bus)
collection.ensure_not_locked()
return [StoredPassword(item)
for item in collection.get_all_items()
if item.get_attributes()['xdg:schema'] ==
'chrome_libsecret_password_schema']
def password_for_site_matching(pattern, all_passwords):
matching = [item for item in all_passwords if pattern in item.realm]
pws = set(m.password for m in matching)
if len(pws) > 1:
print('Found %d passwords across %d password manager entries:')
for m in matching:
print(' %s %s' % (m.username, m.url))
print('Use a more specific match.')
sys.exit(1)
return pws.pop()
def find_matching_passwords(password, site_pattern, all_passwords):
reuse = [item
for item in all_passwords
if item.password == password and (site_pattern is None or site_pattern not in item.realm)]
reuse.sort(key=lambda pw: pw.realm)
if len(reuse):
print('Found password. It is reused across %d sites:' % len(reuse))
username_width = max(len(item.username) for item in reuse)
for item in reuse:
print('{:{width}} {}'.format(item.username,
item.realm,
width=username_width))
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='Find reused passwords')
parser.add_argument(
'--site',
type=str,
help='Find sites reusing a the password from this site')
parser.add_argument('--password',
type=str,
help='Find sites using this password')
args = parser.parse_args()
if not args.site and not args.password:
parser.print_help()
sys.exit(1)
if args.site and args.password:
parser.print_help()
print()
print('You can only pass one of --site and --password')
sys.exit(1)
all_passwords = get_all_passwords()
if args.site:
password = password_for_site_matching(args.site, all_passwords)
else:
password = args.password
find_matching_passwords(password, args.site, all_passwords)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment