Reusing Passwords

I have a confession. Sometimes I reuse passwords. Not for anything that “matters”, but I’ve ended up using a couple of passwords a lot of times. And inevitably some of those sites get hacked. But where did I use them?

Chrome remembers all my passwords but unfortunately doesn’t seem to offer a straightforward API to get at them. Conveniently they do sync my passwords to my computer’s password store and there’s an API for that.

I wrote a little script and I’ve been going through generating unique passwords for all the unimportant sites and turning two-factor authentication on where it’s offered. The Secret Service database (and Chrome) seem to sometimes end up with multiple entries for a single site, and Chrome doesn’t seem to sync my updates immediately, but I’ve found this a helpful start.

#!/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)

view raw
reused.py
hosted with ❤ by GitHub