Wachtwoorden veilig opslaan
Een salt en een trage functie
De dief van les 1 had twee voordelen. Eén gok diende elke gebruiker: 300 digests werden één keer berekend en met de hele tabel vergeleken. En elke gok kostte bijna niets, omdat
Dat zijn twee aparte problemen, en ze hebben twee aparte reparaties. Deze les houdt ze uit elkaar, omdat elke reparatie het andere probleem onaangeroerd laat.
Een salt
Een
De eerste twee regels zijn de tabel van les 1: hetzelfde wachtwoord geeft beide keren dezelfde digest, a353b0deccba3f4e. De laatste twee regels zijn weer dana en gus, met hetzelfde wachtwoord en elk een salt. Hun digests, 335dd0afc19d7c76 en 52e28c68e4587e5e, hebben niets met elkaar gemeen.
Een salt is geen geheim, en geen sleutel. Hij staat leesbaar naast de hash, omdat de server hem weer nodig heeft om een wachtwoord te controleren. Hij moet alleen voor elk record anders en onvoorspelbaar zijn, dus hij komt uit secrets, om de reden die hoofdstuk 2 gaf.
Dit blok staat hier om te laten zien wat een salt doet. Zo worden wachtwoorden niet opgeslagen: de
Waar staat de salt van een record?
Wat een salt voor de dief verandert
Denk terug aan de letmein hangt af van tegen welk record hij geprobeerd wordt, dus er is geen enkele digest om ervoor op te slaan.
De dief moet voor elk record opnieuw beginnen: de salt van dat record nemen, alle 300 woorden ermee hashen, vergelijken. Voor 8 gebruikers is dat 8 keer 300, dus 2.400 digests. Voor 8 miljoen gebruikers is het 2.400 miljoen. Het werk groeit nu met het aantal accounts.
Het tweede lek is ook gedicht. Twee gebruikers met hetzelfde wachtwoord krijgen verschillende salts en dus verschillende digests. Niemand kan aan de tabel zien dat dana en gus hetzelfde kozen.
Elk record heeft nu een eigen salt. 8 gebruikers, 300 woorden: hoeveel digests om elk woord tegen elke gebruiker te proberen?
Wat een salt niet verandert
Neem nu de dief die niet iedereen wil hebben. Hij wil ana.
De salt van ana staat in de tabel, naast haar hash. De dief neemt hem, hasht er 300 woorden mee, en is klaar. De salt heeft wel één ding weggenomen: digests die iemand vóór het lek had voorbereid, zijn nutteloos tegen haar record. Maar nieuwe gokken tegen één record kosten wat ze eerst kostten. Elke gok is nog steeds één snelle aanroep van SHA-256, en een grafische kaart doet er nog steeds een enorm aantal per seconde.
Een salt beantwoordt dus het eerste voordeel, één gok voor iedereen, en laat het tweede met
Een trage functie
De tweede reparatie is elke gok duur maken. Een wachtwoordhash is een functie die zo gebouwd is dat één aanroep met opzet merkbaar veel tijd en geheugen kost.
De server betaalt die prijs één keer per login, waar een paar honderdsten van een seconde er niet toe doen. De dief betaalt hem één keer per gok, tegen elk record. Miljoenen gokken van elk een paar honderdsten van een seconde is iets heel anders dan miljoenen gokken die zo goed als niets kosten.
De standaardbibliotheek van Python heeft zo’n functie, hashlib.scrypt. Dit is één aanroep, als fragment, omdat hij niet op deze pagina draait:
digest = hashlib.scrypt(
b"sunshine2024", salt=dana_salt, n=2**14, r=8, p=1, dklen=32
)
print(digest.hex())
Het drukt 3c0f35b38363436da5b163be671680e2337f6a9ac47bdd4b21504e6c2c0be613 af. De salt is een eigen n, r en p bepalen hoeveel werk één aanroep doet. Met deze instellingen kost een aanroep op deze site ongeveer twee honderdsten van een seconde en heeft hij zolang hij draait ongeveer 16
De twee reparaties naast elkaar:
| Het voordeel van de dief | De reparatie | Wat die verandert |
|---|---|---|
| Eén gok wordt in één keer tegen elk record getest, en gelijke wachtwoorden zijn zichtbaar | Een willekeurige salt voor elk record | Elk record moet apart worden aangevallen |
| Elke gok kost bijna niets | Een trage functie | Elke gok kost tijd en geheugen |
Een wachtwoord is alleen met allebei veilig opgeslagen.
Een dief wil het wachtwoord van één bepaalde gebruiker en heeft het SHA-256-record met salt van die gebruiker. Welke reparatie maakt dit moeilijker?
In de volgende les roep je de trage functie zelf aan.