De evolutie van wachtwoordhashing
Je hebt misschien adviezen gehoord voor het kiezen van wachtwoordhashingalgoritmen, maar heb je nagedacht over waarom ze worden aanbevolen? In dit artikel zullen we de evolutie van wachtwoordhashingalgoritmen verkennen en de redenen erachter.
Inleiding
Wachtwoordhashing, zoals de naam al doet vermoeden, is het proces van het berekenen van een hashwaarde van een wachtwoord. De hashwaarde wordt meestal opgeslagen in een database, en tijdens het inlogproces wordt de hashwaarde van het wachtwoord dat door de gebruiker is ingevoerd berekend en vergeleken met de hashwaarde die in de database is opgeslagen. Als ze overeenkomen, wordt de gebruiker geauthenticeerd.
Voordat we duiken in de evolutie van wachtwoordhashingalgoritmen, is het belangrijk om te begrijpen waarom dit noodzakelijk is.
Platte tekst wachtwoorden: Een groot beveiligingsrisico
Stel je voor dat je een gebruiker bent van een website waar je een account hebt geregistreerd. Op een dag wordt de website gehackt en wordt de database gelekt. Als de website wachtwoorden in platte tekst opslaat, kan de hacker direct toegang krijgen tot je wachtwoord. Aangezien veel mensen wachtwoorden hergebruiken op meerdere websites, kan de hacker dit wachtwoord gebruiken om ongeoorloofde toegang te krijgen tot je andere accounts. De situatie wordt nog erger als je hetzelfde of een soortgelijk wachtwoord voor je e-mailaccount gebruikt, omdat de hacker je wachtwoord kan resetten en al je bijbehorende accounts kan overnemen.
Zelfs zonder een datalek, in grote teams, kan iedereen met toegang tot de database wachtwoorden zien. In vergelijking met andere informatie zijn wachtwoorden zeer gevoelig, en je wilt absoluut niet dat iemand er toegang toe heeft.
Het opslaan van wachtwoorden zonder te hashen is een amateurfout. Helaas, als je zoekt naar "wachtwoordlek platte tekst", zul je ontdekken dat grote bedrijven zoals Facebook, DailyQuiz, en GoDaddy allemaal wachtwoordlekken in platte tekst hebben ervaren. Het is waarschijnlijk dat veel andere bedrijven dezelfde fout hebben gemaakt.
Codering v.s. versleuteling v.s. hashing
Deze drie termen worden vaak verward, maar het zijn verschillende concepten.
Codering
Codering is het eerste dat moet worden uitgesloten voor wachtwoordopslag. Bijvoorbeeld, Base64 is een coderingsalgoritme dat binaire gegevens omzet in een reeks tekens:
Als men het coderingsalgoritme kent, kan men de gecodeerde string decoderen en de oorspronkelijke gegevens terughalen:
Voor hackers zijn de meeste coderingsalgoritmen gelijk aan platte tekst.
Versleuteling
Voordat hashing populair werd, werd versleuteling gebruikt om wachtwoorden op te slaan, zoals met AES. Versleuteling houdt in het gebruik van een sleutel (of een paar sleutels) om gegevens te versleutelen en te ontsleutelen.
Het probleem met versleuteling is duidelijk in de term "ontsleutelen". Versleuteling is omkeerbaar, wat betekent dat als een hacker de sleutel bemachtigt, ze het wachtwoord kunnen ontsleutelen en het oorspronkelijke wachtwoord kunnen terughalen.
Hashing
Het belangrijkste verschil tussen hashing, codering en versleuteling is dat hashing onomkeerbaar is. Zodra een wachtwoord is gehasht, kan het niet worden teruggedraaid naar zijn oorspronkelijke vorm.
Als eigenaar van een website hoef je het wachtwoord zelf niet te kennen, zolang de gebruiker kan inloggen met het juiste wachtwoord. Het registratieproces kan als volgt worden vereenvoudigd:
- Gebruiker voert het wachtwoord in.
- De service gebruikt een hash-algoritme om de hashwaarde van het wachtwoord te berekenen.
- De service slaat de hashwaarde op in de database.
Wanneer de gebruiker inlogt, is het proces:
- Gebruiker voert het wachtwoord in.
- De service gebruikt hetzelfde hash-algoritme om de hashwaarde van het wachtwoord te berekenen.
- De service vergelijkt de hashwaarde met de hashwaarde die in de database is opgeslagen.
- Als de hashwaarden overeenkomen, wordt de gebruiker geauthenticeerd.
Beide processen voorkomen het opslaan van wachtwoorden in platte tekst, en aangezien hashing onomkeerbaar is, zelfs als de database is gecompromitteerd, kan de hacker alleen hashwaarden verkrijgen die eruitzien als willekeurige strings.
Starterspakket voor hash-algoritmen
Hashing lijkt misschien de perfecte oplossing voor wachtwoordopslag, maar het is niet zo eenvoudig. Om te begrijpen waarom, laten we de evolutie van wachtwoordhashingalgoritmen verkennen.
MD5
In 1992 ontwierp Ron Rivest het MD5-algoritme, een bericht-digest algorithm dat een 128-bit hashwaarde kan berekenen van willekeurige gegevens. MD5 is veel gebruikt in verschillende velden, inclusief wachtwoordhashing. Bijvoorbeeld, de MD5-hashwaarde van "123456" is:
Zoals eerder vermeld, lijkt de hashwaarde op een willekeurige string en is onomkeerbaar. Bovendien is MD5 snel en eenvoudig te implementeren, wat het tot het populairste wachtwoordhashingalgoritme maakte.
Echter, de voordelen van MD5 zijn ook zijn zwakheden bij wachtwoordhashing. Zijn snelheid maakt het kwetsbaar voor brutekracht aanvallen. Als een hacker een lijst heeft van veelvoorkomende wachtwoorden en je persoonlijke informatie, kunnen ze de MD5-hashwaarde berekenen van elke combinatie en deze vergelijken met de hashwaarden in de database. Bijvoorbeeld, ze kunnen je verjaardag combineren met je naam of de naam van je huisdier.
Tegenwoordig zijn computers aanzienlijk krachtiger dan vroeger, waardoor het gemakkelijk is om brutekracht MD5-wachtwoord hashes te forceren.
SHA familie
Waarom zou je dan geen ander algoritme gebruiken dat langere hashwaarden genereert? De SHA-familie lijkt een goede keuze. SHA-1 is een hashingalgoritme dat 160-bit hashwaarden genereert, en SHA-2 is een familie van hashingalgoritmen die hashwaarden van 224-bits, 256-bits, 384-bits en 512-bits lengtes genereren. Laten we de SHA-256-hashwaarde bekijken van "123456":
De SHA-256-hashwaarde is veel langer dan die van MD5 en is ook onomkeerbaar. Er is echter een ander probleem: als je de hashwaarde al kent, zoals hierboven, en je ziet exact dezelfde hashwaarde in de database, weet je dat het wachtwoord "123456" is. Een hacker kan een lijst maken van veelvoorkomende wachtwoorden en hun overeenkomstige hashwaarden en deze vergelijken met de hashwaarden in de database. Deze lijst staat bekend als een regenboogtabel.
Zout
Om regenboogtabelaanvallen te verminderen, werd het concept van zout geïntroduceerd. Zout is een willekeurige string die aan het wachtwoord wordt toegevoegd voordat het wordt gehasht. Als het zout bijvoorbeeld "zout" is, en je wilt SHA-256 gebruiken om het wachtwoord "123456" met het zout te hashen, doe je in plaats van simpelweg:
zou je doen:
Zoals je kunt zien, is het resultaat compleet anders dan hashen zonder zout. Meestal wordt elke gebruiker tijdens de registratie een willekeurige zout toegewezen, die samen met de hashwaarde in de database wordt opgeslagen. Tijdens het inlogproces wordt het zout gebruikt om de hashwaarde van het ingevoerde wachtwoord te berekenen, die vervolgens wordt vergeleken met de opgeslagen hashwaarde.
Iteratie
Ondanks de toevoeging van zout is de hashwaarde nog steeds gevoelig voor brutekracht aanvallen als hardware krachtiger wordt. Om het moeilijker te maken, kan iteratie (dat wil zeggen, het algoritme meerdere keren uitvoeren) worden geïntroduceerd. Bijvoorbeeld, in plaats van te gebruiken:
zou je het kunnen doen:
Het verhogen van het aantal iteraties maakt bruteforcen moeilijker. Dit beïnvloedt echter ook het inlogproces, aangezien het trager wordt. Daarom moet er een balans worden gevonden tussen veiligheid en prestaties.
Halftijd pauze
Laten we even pauzeren en de kenmerken samenvatten van een goed wachtwoordhashingalgoritme:
- Onoomkeerbaar (preimage resistentie)
- Moeilijk te brute forcen
- Bestand tegen regenboogtabelaanvallen
Zoals je misschien hebt gemerkt, zijn zout en iteratie noodzakelijk om aan al deze vereisten te voldoen. Het probleem is dat zowel MD5 als de SHA-familie niet specifiek zijn ontworpen voor wachtwoordhashing; ze worden veel gebruikt voor integriteitscontroles (of "bericht-digest"). Als resultaat kan elke website zijn eigen implementatie van zout en iteratie hebben, wat standaardisatie en migratie uitdagend maakt.
Wachtwoordhashingalgoritmen
Om dit probleem aan te pakken, zijn er verschillende hash-algoritmen specifiek ontworpen voor wachtwoordhashing. Laten we eens kijken naar enkele van hen.
bcrypt
bcrypt is een wachtwoordhashingalgoritme ontworpen door Niels Provos en David Mazières. Het wordt veel gebruikt in veel programmeertalen. Hier is een voorbeeld van een bcrypt-hashwaarde:
Hoewel het lijkt op een andere willekeurige string, bevat het extra informatie. Laten we het opsplitsen:
- Het eerste gedeelte
$2y
geeft het algoritme aan, dat is2y
. - Het tweede gedeelte
$12
geeft het aantal iteraties aan, dat is12
. Dit betekent dat het hashingalgoritme 212=4096 keer (iteraties) wordt uitgevoerd. - Het derde gedeelte
wNt7lt/xf8wRJgPU7kK2ju
is het zout. - Het laatste gedeelte
GrirhHK4gdb0NiCRdsSoAxqQoNbiluu
is de hashwaarde.
bcrypt heeft enkele beperkingen:
- De maximale lengte van het wachtwoord is 72 bytes.
- Het zout is beperkt tot 16 bytes.
- De hashwaarde is beperkt tot 184 bits.
Argon2
Gezien de debatten en beperkingen van bestaande wachtwoordhashingalgoritmen, werd er in 2015 een wachtwoordhashingwedstrijd gehouden. Zonder in details te treden, laten we ons richten op de winnaar: Argon2.
Argon2 is een wachtwoordhashingalgoritme ontworpen door Alex Biryukov, Daniel Dinu, en Dmitry Khovratovich. Het introduceert verschillende nieuwe concepten:
- Geheugen-hard: Het algoritme is ontworpen om moeilijk te parallelliseren, waardoor bruteforcing met GPU's uitdagend wordt.
- Tijd-hard: Het algoritme is ontworpen om moeilijk te optimaliseren, waardoor bruteforcing met ASICs (Toepassingsspecifieke geïntegreerde schakelingen) moeilijk wordt.
- Weerstand tegen zij-kanaal: Het algoritme is ontworpen om bestand te zijn tegen zij-kanaal aanvallen, zoals timingaanvallen.
Er zijn twee hoofdversies van Argon2, Argon2i en Argon2d. Argon2i is het veiligst tegen zij-kanaal aanvallen, terwijl Argon2d de hoogste weerstand biedt tegen GPU kraakaanvallen.
-- Argon2
Hier is een voorbeeld van een Argon2-hashwaarde:
Laten we het opsplitsen:
- Het eerste gedeelte
$argon2i
geeft het algoritme aan, dat isargon2i
. - Het tweede gedeelte
$v=19
geeft de versie aan, die is19
. - Het derde gedeelte
$m=16,t=2,p=1
geeft de geheugenkosten aan, tijdkosten, en mate van parallellisatie, die zijn16
,2
, en1
. - Het vierde gedeelte
$YTZ5ZnpXRWN5SlpjMHBDRQ
is het zout. - Het laatste gedeelte
$12oUmJ6xV5bIadzZHkuLTg
is de hashwaarde.
In Argon2 is de maximale lengte van het wachtwoord 232-1 bytes, het zout is beperkt tot 232-1 bytes, en de hashwaarde is beperkt tot 232-1 bytes. Dit zou in de meeste scenario's voldoende moeten zijn.
Argon2 is nu beschikbaar in veel programmeertalen, zoals node-argon2 voor Node.js en argon2-cffi voor Python.
Conclusie
In de loop der jaren hebben wachtwoordhash-algoritmen een significante evolutie ondergaan. We zijn veel dank verschuldigd aan de beveiligingsgemeenschap voor hun decennia lange inspanningen om het internet veiliger te maken. Dankzij hun bijdragen kunnen ontwikkelaars zich meer richten op het bouwen van betere diensten zonder zich zorgen te hoeven maken over de beveiliging van wachtwoordhashing. Hoewel het bereiken van 100% veiligheid in een systeem mogelijk onhaalbaar is, kunnen we diverse strategieën toepassen om de bijbehorende risico's te minimaliseren.
Als je de rompslomp van het implementeren van authenticatie en autorisatie wilt vermijden, probeer dan Logto gratis. Wij bieden veilige (we gebruiken Argon2!), betrouwbare, en schaalbare oplossingen, zodat je je kunt concentreren op het bouwen van je product.