Navigatie overslaan.
Start

Password hashing en salting

Ik snap dus niet hoe je dit nog steeds verkeerd kan doen, tenzij je alles zelf probeert uit te vinden met een significant gebrek aan elementaire kennis of logisch denkvermogen.

Misschien zou het ook kunnen omdat er geen Nederlandstalig uitleg is over hoe je wachtwoorden correct opslaat?
Laten we het daar op houden, want als je nu de moeite doet om het even op te zoeken, zou je hier moeten uitkomen en is er vanaf nu geen enkele reden meer niet meer dezelfde capitale fouten te maken die al honderden keren in het verleden werden gemaakt. (Dus ook niet blindelings code kopiëren van websites.)

Wachtwoorden

Vaak is er ergens een database/tekstfile/LDAP/XML-bestand/etc waarin de gebruikersnaam, wachtwoord en nog wat extra informatie in wordt opgeslagen, bijvoorbeeld:

johan:wachtwoord
mephistopheles:geheimpje
...

Makkelijk, maar uiteraard is het erg dom om gegevens op deze manier op te slaan en eventueel zelfs strafbaar.
Toch spelen sommige bedrijven het nog steeds klaar om dit te doen: http://www.itsecurity.be/sony-hacked-again-including-belgian-and-dutch-w...

Hashing

Veel beter is om de wachtwoorden eerst via een hash-functie te laten passeren en enkel de resulterende hash-value op te slaan.

Het resultaat is dan bijvoorbeeld:

johan:701f33b8d1366cde9cb3822256a62c01
mephistopheles:39d642d0703e48248bdf85f8680751d1
...

Om te controleren of een gebruiker het juiste wachtwoord heeft ingevoerd, wordt het ingevoerde wachtwoord via hetzelfde algoritme gehashed en worden de hashes met elkaar vergeleken.

Hierbij zijn er echter wel enkele probleempjes:

Valt je hier iets op?

johan:701f33b8d1366cde9cb3822256a62c01
mephistopheles:39d642d0703e48248bdf85f8680751d1
...
paarsgeelkriebelbeest:202cb962ac59075b964b07152d234b70
ferdinant:39d642d0703e48248bdf85f8680751d1

Inderdaad, de gebruiker "mephistopheles" en "ferdinant" gebruiken (toevallig of niet) hetzelfde wachtwoord.
m.a.w. als ik 1 wachtwoord kan achterhalen, weet ik dat dit zal werken voor beide gebruikers.

Als het algoritme gekend is (en dat is vrij makkelijk aangezien de structuur en gebruikte tekens van de hashes dat verraden) dan kan ik zogenaamde rainbowtables opstellen. (Het bovenstaande voorbeeld gebruikt trouwens MD5 hashes.)

Rainbowtables zijn grote gegevensbestanden, giga- en terrabytes aan data, waarbij voor de meest voorkomende wachtwoorden, of zelfs voor alle mogelijk wachtwoorden, de hash value al is berekend en opgeslagen.

Als ik ergens een hash-value van "202cb962ac59075b964b07152d234b70" tegenkom, kan ik dus simpelweg in deze rainbowtable kijken welke wachtwoord een zulke hashvalue kan genereren.
Hierdoor kan ik dus achterhalen dat het wachtwoord voor gebruiker "paarsgeelkriebelbeest" "123" is.

(Merk op dat dit niet noodzakelijk hetzelfde wachtwoord is, aangezien (non-perfect) hash algoritmes een oneindig-op-een relatie hebben.
Er zijn dus (in theorie) een oneindig aantal wachtwoorden die dezelfde hashvalue genereren.

Met het bovenstaande in gedachte is er ook een tweede attack mogelijk.
Sommige hash functies, zoals MD5, zijn vatbaar voor collision en pre-image attacks.
Hierdoor is het mogelijk om op basis van de MD5 hashvalue een bijhorend wachtwoord te vinden zonder rainbowtables te gebruiken.

Voor tools: http://www.mathstat.dal.ca/~selinger/md5collision/

Het gebruik van MD5 als hashing algoritme wordt al sinds 1996 afgeraden en sindsdien zijn de mogelijke aanvallen alleen maar verbeterd.
Durf er dus niet eens aan te denken om MD5 te gebruiken als hash algoritme.
SHA-1 wordt trouwens ook al een tijdje afgeraden.
Vandaag wordt SHA-256 als minimum gezien voor hash functies, dus...

Alhoewel sterkere algoritmes mathematische attacks moeilijker maken, is een aanval via rainbowtables altijd mogelijk bij het gebruik van "normale" hashes.

Salts

Uiteraard is er ook een oplossing om het gebruik van rainbowtables tegen te gaan en dat is password salting.
In plaats van het wachtwoord rechtstreeks te hashen, wordt er een tekenreeks (de salt) aan toegevoegd, en de combinatie van salt en wachtwoord wordt gehashed. (Of dat nu vooraan, of aan het einde, of middenin, of op welke manier dan ook gebeurd, maakt niet uit. De essentie is dat niet enkel het wachtwoord gehashed wordt.)

Nu zijn er natuurlijk weer goede manieren en een heleboel slechte manieren om aan salting te doen, en zoals gewoonlijk gebruikt men in de IT industrie uiteraard vooral de slechte.

Slechte manier 1:

Men gebruikt een "geheime" waarde die ergens in een applicatie hard-coded is.
Dit is eigenlijk geen salt, maar een "key" en valt onder "Security-by-Obscurity".
Het lost niets op en geeft enkel een vals gevoel van veiligheid.
Gebruikers met hetzelfde wachtwoord hebben nog steeds dezelfde hashvalues, etc...

Slechte manier 2:

Nog een verkeerde manier is om de loginnaam als salt te gebruiken.
Nu hebben welliswaar verschillende gebruikers met hetzelfde wachtwoord niet meer dezelfde hash-value, maar "speciale" rainbow tables zijn nog steeds een optie.

"Speciaal", want ze bevatten enkel entries voor "speciale" gebruikers.
Ik ben er vrij zeker van dat er een "root" gebruiker is in Linux/Unix en een "Administrator" in Windows.

Ook is een username op het internet vaak een email adres, er worden veel bedrijven per dag gehacked.
De gestolen gegevens worden bijna altijd verkocht en uitgewisseld, en als er ergens 1 website is waarbij wachtwoorden leesbaar opgeslagen worden (bijvoorbeeld die van Sony), is het een fluitje van een cent om de gevonden wachtwoorden te hashen en na te gaan of er ergens in een ander gehacked bedrijf hetzelfde wachtwoord gebruikt wordt.
Zelfs als alle sites een hash met username salt zouden gebruiken, is het nog steeds mogelijk om te zien of ergens hetzelfde wachtwoord gebruikt wordt.

Goede manier:

De enige juiste manier om salt toe te passen in door voor ieder wachtwoord een unieke salt te genereren.
Dat wil zeggen groot genoeg en random genoeg om met een aan de zekerheid grenzende waarschijnlijkheid te kunnen beweren dat de salt uniek is in het universum.
Verder moet, en dat wordt ook vaak vergeten, de salt ook mee veranderen als de gebruiker zijn wachtwoord wijzigt.

Het is dus niet de bedoeling dat deze salt "geheim" is. Deze wordt mee opgeslagen met het wachtwoord:

johan:willekeurigesalt:6b303a4f54b9aada93aed5383ba930ef

Op een ander systeem, met dezelfde username en hetzelfde wachtwoord, ziet er dat zo uit:

johan:anderesalt:806679e083954d2b218cc1f342c4aa1f

Om te controleren of het opgegeven wachtwoord klopt, wordt de opgeslagen salt toegevoegd aan het ingevoerde wachtwoord, de combinatie wordt doorheen het hashing algoritme gejaagd, en het resultaat daarvan wordt met de opgeslagen hash vergeleken. (In dit geval wordt de salt vlak voor het ingevoerde wachtwoord geplaatst.)

Bij even sterke hashing algoritmes, is het een voordeel om het meest trage te kiezen, of om hetzelfde algoritme meerdere keren te gebruiken.
Het genereren van een hash value is echt niet de traagste component in het gehele inlogproces, en het is mogelijk om dit zelfs 10 000 keer te doen.
Dit voegt niets toe aan de beveiliging van het systeem, maar maakt een eventuele brute force attack, of het genereren van rainbow tables, wel met een magnitude trager.

Uiteraard is deze oefening compleet zinloos als er geen deftige password policy aanwezig is waardoor gebruikers nog steeds "123" of "wachtwoord" als wachtwoord kunnen gebruiken...