Hoofdstuk 3 · oefening
Wachtwoorden veilig opslaan
Wachtwoorden terugvinden uit snelle hashes
Bevinding F1 staat nog open: de leeslijst slaat elk wachtwoord op als platte tekst. Hoofdstuk 2 zei dat een hash niet terug te draaien is. Dus hier is een verleidelijke oplossing: sla hashlib.sha256(password) op en nooit het wachtwoord zelf. Een dief die de state kopieert, vindt digests, en digests kun je niet terugzetten.
Deze les speelt de dief tegen precies dat ontwerp, en het ontwerp verliest.
Eerst iets over het project van dit hoofdstuk. Je server heeft één verandering die nog niets nieuws doet: de beslissing over een login zit nu in een eigen login_user(state, credentials), die handle() aanroept. Er zijn twee nieuwe constanten in reading_server.py, ANA_RECORD en BEN_RECORD, die op les 6 wachten. En er is een nieuw bestand, passwords.py, waarin je het grootste deel van het hoofdstuk werkt.
Het lek
Het project bevat een bestand dat voor een lek staat: leaked_table.json. Het komt van een verzonnen boekenclubsite die voor elke gebruiker een
cat leaked_table.json
Acht gebruikers, acht digests van 64 hextekens, en nergens een wachtwoord te zien. De dief is hier de derde aanvaller uit hoofdstuk 1. Hij heeft het bestand en stuurt geen verzoeken, dus niets op de server remt hem af. Hij werkt op zijn eigen machine, zo lang als hij wil.
Raden en controleren
Een digest kun je niet terugzetten, en dat hoeft ook niet. De dief kan een gok hashen en vergelijken. Als hashlib.sha256(b"letmein").hexdigest() gelijk is aan een digest in de tabel, is het wachtwoord van die gebruiker letmein.
De dief heeft alleen goede gokken nodig, en mensen zijn voorspelbaar. words.txt bevat 300 gokken van het soort dat mensen kiezen: een woord, soms gevolgd door een jaartal, 123 of !. Het bestand begint met reading, reading1, reading12. Het is voor deze cursus geschreven volgens een eenvoudige regel: twintig basiswoorden, elk met vijftien uitgangen.
Eén keer door de woorden
Dit is het idee dat deze aanval goedkoop maakt. Hash elk woord één keer, en bewaar de resultaten in een
De 300 digests worden één keer berekend en dienen alle acht gebruikers. Ze zouden acht miljoen gebruikers net zo goed dienen. Het werk van de dief groeit met het aantal gokken en niet met het aantal accounts.
De tabel heeft 8 gebruikers en de lijst heeft 300 woorden. Hoeveel SHA-256-digests moet de dief berekenen om elk woord tegen elke gebruiker te proberen?
Je opdracht
Schrijf in passwords.py de functie crack(table, words).
tableis een dict van gebruikersnaam naar de SHA-256-hexdigest van het wachtwoord van die gebruiker.wordsis eenvan om te proberen. Hash elk woord als
hashlib.sha256(word.encode("utf-8")).hexdigest().Geef een dict terug van gebruikersnaam naar wachtwoord, voor elke gebruiker wiens digest bij een woord past. Laat de gebruikers weg die je niet hebt teruggevonden.
Gebruik alleen de twee main() onder aan het bestand leest leaked_table.json en words.txt en roept je functie aan. Voer het daarna uit:
python passwords.py
Het resultaat lezen
amir bookworm123
bea coffee!
chidi letmein
dana sunshine2024
eli (not recovered)
farah dragon99
gus sunshine2024
hana (not recovered)
6 of 8 passwords recovered from 300 words
Zes van de acht accounts, uit 300 gokken, in de tijd die het kost om op Enter te drukken.
Kijk naar dana en gus. Ze hebben hetzelfde wachtwoord, en dat had je kunnen weten voordat je iets kraakte: hun digests in de tabel zijn gelijk. Dezelfde invoer geeft altijd dezelfde digest. Dat maakte een digest een goede
eli en hana hebben het overleefd. Hun wachtwoorden staan niet in de lijst, en dat is de enige reden. De hash heeft ze niet beschermd. Een langere lijst zou ze misschien vinden.
SHA-256 is niet gebroken, en het is nog steeds het juiste gereedschap voor een ETag. Het is het verkeerde gereedschap voor een wachtwoord, om één reden: het is gebouwd om snel te zijn. Eén grafische kaart kan elke seconde een enorm aantal SHA-256-gokken proberen, en een echte woordenlijst heeft vele miljoenen regels, geen 300.
dana en gus hebben dezelfde digest in de tabel. Wat weet een dief voordat hij iets raadt?
Een gewone digest opslaan dicht F1 dus niet. De dief van deze les had twee voordelen: één gok diende elke gebruiker, en elke gok kostte bijna niets. De volgende les neemt ze allebei weg.
De regel van deze cursus geldt net zo goed voor wat je vandaag schreef als voor al het andere. Deze tabel is voor de cursus verzonnen. Voer crack nooit uit op data die niet van jou is.
Klik op Check my work als python passwords.py zes wachtwoorden afdrukt.
Opdracht
Schrijf in passwords.py de crack(table, words).
tableis eenvan gebruikersnaam naar de -hexdigest van het wachtwoord van die gebruiker. wordsis eenvan om te proberen. Geef een dict terug van gebruikersnaam naar wachtwoord voor elke gebruiker wiens digest bij een woord past, gehasht als
hashlib.sha256(word.encode("utf-8")).hexdigest().Laat gebruikers weg die je niet hebt teruggevonden.
Gebruik alleen de twee
. main()leest de bestanden voor je.
Voer python passwords.py uit in de 6 of 8 passwords recovered from 300 words.
Klik daarna op Check my work. Die roept crack() aan met een tabel en een woordenlijst die je niet hebt gezien: vijf gebruikers en zes woorden, waarbij twee gebruikers een wachtwoord delen, één wachtwoord niet tussen de woorden staat, en één letters buiten ASCII heeft. Het resultaat moet precies de teruggevonden gebruikers bevatten, elk met zijn wachtwoord, en de tabel die je kreeg, moet ongewijzigd blijven. Zonder woorden, of zonder gebruikers, moet het resultaat een lege dict zijn. De twee bestanden in je project worden niet gebruikt, en niets uit je terminal wordt beoordeeld.