Navigatie overslaan.
Start

Wachtwoorden opslaan

Als je een applicatie schrijft en users wil laten authentificeren, dan gebeurt dit vaak met wachtwoorden.
Op zich is hier natuurlijk niets mis mee, maar toch zijn veel developpers een beetje achterlijk omdat ze wachtwoorden opslaan, al of niet "vervormd".

Dat doe je natuurlijk niet. Je slaat nooit of te nooit een wachtwoord op.
Zelfs een "hash" sla je niet op als je aan beveiliging denkt, maar een "salted hash".

Waarom?
Daarom.

Hoe het niet moet.

Wachtwoorden opslaan is sowieso uit den boze.
Het is namelijk een groot security risico en je hebt dat nergens voor nodig.

Als je als developer denkt dat je slim bent door ROT-13 of een andere vorm van caesariaanse substitutie toe te passen, dan ben je verkeerd.
Nog erger is een eigen algoritme "uitvinden" om het wachtwoord "onleesbaar" te maken. Je bent dan namelijk je tijd aan het verliezen en je snapt het issue niet.

Alle methodes waarbij het opgeslagen wachtwoord "gereversed" kan worden, zijn fundamenteel verkeerd.

Een bekend voorbeeld van een dergelijk issue is Cisco met hun "password 7" "encryptie".
Dergelijke wachtwoorden zijn omkeerbaar.

Op zich zou je denken dan configuratiefiles sowieso een beetje geheimgehouden worden, maar google zegt van niet:

Cisco config:
...
enable password 7 8338kgjgjg9r9rkf90d
...
Interface Ethernet0
ip address 195.95.181.253 255.255.255.0
...
Whois info:
inetnum:        195.95.181.0 - 195.95.181.255
netname:        TDC-KABELTV-TVOIP-SRC-NET
descr:          Source addresses for TVoIP
descr:          TDC Kabel TV
country:        dk

Je hebt het IP adres en je hebt het wachtwoord.
Hoe kan het nu dat er ingebroken wordt?

(Zie ook http://blog.knudde.be/Alles_staat_online om alles te vinden op het internet...)

Hoe kunnen we dat nu beter doen?

We kunnen al eens beginnen met een hash op te slaan.
Een md5sum bijvoorbeeld.

Als het wachtwoord "johan" zou zijn, dan is de md5sum "0cbf9db853f0ff7984a71d50e876ba02".

Hash functies kenmerken zich doordat het makkelijk is om een conversie te doen van A naar B, maar praktisch onmogelijk van B naar A.

Om te verifiëren of een wachtwoord correct is, dan wordt het wachtwoord met hetzelfde algoritme "gehashed" en vergeleken met de opgelagen hash.
Komen beide waardes overeen, dan was het wachtwoord juist, anders niet.

Een eerste opmerking is dat voor een kleine wijziging in het wachtwoord, de hash er volledig anders uitziet: "Johan" heeft bijvoorbeeld "3fcf874311a4ab24eefca45af755c795" als hash. Een hoodletter geeft een totaal andere output, zoals het hoort bij goede hashfuncties.

Een tweede opmerking is dat er een oneindig aantal A's (input) zijn, maar slechts een eindig aantal B's (output), aangezien de output een vast aantal bytes heeft en een beperkte tekenreeks.
Als je 2 A's hebt die dezelfde B als resultaat geven, dan heb je een collision gevonden en dit is een attackvector.

Een collision vinden is echter moeilijk en erg mathematisch.
Momenteel is het nog niet mogelijk om dat op een makkelijke manier te doen, alhoewel er de laatse jaren voor md5 (en ander hashfuncties) toch een aantal shortcuts gevonden zijn.

Een groot probleem met hashes is dat als 2 gebruikers toevallig hetzelfde wachtwoord kiezen, dat de hash hetzelfde is. Dit wil dus zeggen dat de ene gebruiker weet dat de andere gebruiker hetzelfde wachtwoord heeft, aangezien het vrij vaak voorkomt dat je als gebruiker van een systeem de hashes kan bekijken.

Nog een issue is dat hashes niet zo willekeurig zijn als men denkt.

Veel mensen gebruiken bijvoorbeeld geboortedatums, of bestaande woorden als wachtwoord. En daar zijn er maar een beperkt aantal van.

Ik kan dus een tabel maken van alle mogelijke datums en alle bestaande woorden, en daarvoor de md5sum berekenen.
Dit vergt natuurlijk een beetje tijd en veel schijfruimte, maar is doenbaar. Men noemt een dergelijke table een "rainbowtable".
Als ik ergens een hash vind die als "4c174fc51d5475245fc55c95cde4a7bd" luidt, dan kan ik in mijn rainbowtable opzoeken dat de input "chocolade" was.

Rainbowtables maken het dus mogelijk om zwakke wachtwoorden zonder al te veel problemen te vinden.

Ik kan bij het creëren van mijn rainbowtable natuurlijk zo ver gaan als ik wil, en ook het hashresultaat van hoofdlettersubstituties e.d. berekenen.

Als je ooit eraan denk om hashes te implementeren voor licence keys, dan kan ik je alvast vertellen dat je niet veel gaat verdienen als je het op de onderstaande manier doet:

Hashes+, Een Salted Hash

Een oplossing voor dergelijke problemen is een salted hash. I.p.v. het wachtwoord the hashen, gaat het systeem een aantal tekens toevoegen aan het wachtwoord. Dit kan ervoor, erna, ertussenin, een combinatie zijn. En dat resultaat wordt dan gehashed.

Er zijn verschillende systemen van salted hashes.

  • Er wordt bij de installatie een random "salt" gekozen, en deze wordt voor alle gebruikers gebruikt. Ieder systeem heeft dus een andere salt.
  • Er wordt per gebruiker een random "salt" gekozen, en deze blijft steeds gelijk.
  • Er wordt per gebruiker een random "salt" gekozen, en deze wordt vaak gewijzigd.

Een groot voordeel van salted hashes is dus dat:

  1. Het niet steeds duidelijk is als meerdere gebruikers hetzelfde wachtwoord hebben, hetgeen brute force attacks enorm vertraagd voor meerdere gebruikers.
  2. Als de salt onbekend is, het quasi onmogelijk is om een rainbowtable te maken.
  3. Als de salt vaak wijzigd, het onmogelijk is om te zeggen of het wachtwoord gewijzigd werd of niet.

Salts hoeven zelfs niet eens geheim gehouden te worden, alhoewel dat ook zijn voordelen heeft.

En nu komt het:

Unix, in lang vervlogen tijden:

Passwords are being crypted using the DES algorithm. DES (`Data Encryption Standard') is an American encryption standard since 1979. With DES it is possible to encrypt and decrypt data using a key.

UNIX
password encryption uses the DES algorithm 25 times in a row.
The first DES round uses 64 0-bits as input and encrypts them with the password the user inputs, with a permutation taking place during the encryption process.

There are 4096 possible permutations. The permutation used is randomly chosen for each user. The chosen permutation is coded into two bytes called `salt'.

The salt is stored in the passwordfile. The output is used as input for the next DES round, which uses the same key and permutation.

This process repeats until there is a final output from the 25th DES round. This output is coded into eleven bytes which are put in the passwordfile. So the coded password data in the passwordfile consists of thirteen bytes, first the salt and then the encrypted password.

Windows, nu:

"HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\LSA\AllowPasswordExport"

E is zelfs een "Password Export Server" (PES) beschikbaar, om het toch maar zo handig mogelijk te maken om wachtwoorden (niet de hashes) te exporteren...

Chocolade

Zie ook: http://www.schneier.com/blog/archives/2008/04/giving_up_passw.html

Ik was weer voor op mijn tijd. :-)