Wanneer je een webapp bouwt waarin mensen met een gebruikersnaam en wachtwoord moeten kunnen inloggen, besteed dan in ieder geval veel aandacht aan het versleutelen van het wachtwoord. Dit zorgt niet alleen voor "discretie" bij jouwzelf (je kunt nooit zien wat iemands wachtwoord is, alleen het versleutelde exemplaar), maar het zorgt er ook voor dat áls het gebeurt dat de gebruikersnamen en wachtwoorden op straat komen te liggen, deze niet te misbruiken zijn.
Via deze pagina vind je een handreiking voor het versleutelen van wachtwoorden.
Dit versleutelen gebeurt door eenmalig "zout" te creëren, dit aan het wachtwoord van iemand vast te plakken en vervolgens het wachtwoord gecombineerd met het zout 50000 keer te versleutelen met behulp van SHA265 (265bits encryptie).
Werk je met een database met gebruiksaccounts, dan is het de bedoeling dat er voor elke nieuwe gebruiker eenmalig "zout" wordt gecreëerd. Een tabel waarin gebruikersaccounts zouden worden opgeslagen zou er dan zo uit kunnen zien:
Herhaling: de kolom "wachtwoord" bevat het wachtwoord met vastgeplakt zout dat 50000 keer versleuteld is. De kolom "zout" is nodig voor als iemand wil inloggen.
Een inlogproces zou er dan zo uit zien:
Stel, je hebt een wachtwoord "DingFlofBips"
En er is willekeurig zout gecreëerd: "462a8678214a5a26"
Als je "DingFlofBips" aan "462a8678214a5a26" vastplakt, krijg je "DingFlofBips462a8678214a5a26"
Na 1 versleuteling wordt dit: "bf1c809c8a18adcbd4cac2272d9318510c02da0ef86985d508db092f627d103f"
En na 50000 keer versleutelen wordt het: "768408da39df613512dd94cf5b5af1185f5818ce81b8e15ade6b624e748646ec". Dit exemplaar stop je in je database, evenals het oorspronkelijke zout "462a8678214a5a26". Geen mens of computer die dit zo 1-2-3 kan terugredeneren naar het wachtwoord van de gebruiker! Het zal echter wel altijd verifieerbaar blijven.
Bovenstaand testje is gebaseerd op deze code:
<?php
# Vul hieronder een wachtwoord in:
$password = "DingFlofBips";
printf("Stel, je hebt een wachtwoord \"%s\" <br />", $password);
# Dit is een voorbeeld van hoe zout kan worden gecreëerd:
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
printf("En er is willekeurig zout gecreëerd: \"%s\" <br />", $salt);
# Op deze manier kun je het zout vastplakken aan het wachtwoord
$salted_password = $password . $salt;
printf("Als je \"%s\" aan \"%s\" vastplakt, krijg je \"%s\" <br />", $password, $salt, $salted_password);
# Hieronder wordt de een variabele voorbereid voor de versleutelde wachtwoord-zout combinatie (dit is de eerste keer versleuteling)
$salted_encrypted_password = hash('sha256', $salted_password);
printf("Na 1 versleuteling wordt dit: \"%s\" <br />", $salted_encrypted_password);
# Nu wordt het versleutelde combinatie nog eens 49999 maal versleuteld
for($round = 1; $round < 50000; $round++){
$salted_encrypted_password = hash('sha256', $salted_encrypted_password);
}
printf("
En na 50000 keer versleutelen wordt het: \"%s\".
Dit exemplaar stop je in je database, evenals het oorspronkelijke zout \"%s\".
Geen mens of computer die dit zo 1-2-3 kan terugredeneren naar het wachtwoord van de gebruiker!
Het zal echter wel altijd verifieerbaar blijven.<br />", $salted_encrypted_password, $salt);
?>
Hieronder zie je een voorbeeld van een inlogcode die met versleuteling werkt:
<?php
# Beveiligingsmaatregel: als er niks is ingevuld, stop dan.
if(empty($_POST)) exit;
# Ingevulde gebruikersnaam en wachtwoord inventariseren:
$username = $_POST['username'];
$password = $_POST['password'];
# Kijken of de gebruiker voorkomt:
$stmt = mysqli_prepare($link, "SELECT id, password, salt FROM users WHERE username = ?");
mysqli_stmt_bind_param($stmt, "s", $username);
mysqli_stmt_execute($stmt);
mysqli_stmt_bind_results($stmt, $id, $encrypted_pwd, $salt);
mysqli_stmt_fetch($stmt);
# Als $id, $encrypted_pwd en $salt gevuld zijn, betekent het dat de gebruiker in ieder geval bestaat.
if($id && $encrypted_pwd && $salt){
# Eerst het ingevulde wachtwoord met het zout plakken en 1x encrypten
$check_password = hash('sha256', $password . $salt);
# Nu wordt de versleutelde combinatie nog eens 49999 maal versleuteld
for($round = 1; $round < 50000; $round++){
$check_password = hash('sha256', $check_password);
}
# Komt het 50000 maal versleutelde wachtwoord overeen met dat in de database? Dan mag de gebruiker inloggen!
if($check_password === $encrypted_pwd){
# De gebruiker mag inloggen want het wachtwoord klopt met datgene in de database!
}
else{
# Het ingevulde wachtwoord klopt niet. Toon een algemene foutmelding
echo "Gebruikersnaam of wachtwoord klopt niet.";
exit;
}
}else{
# Gebruiker bestaat niet. Toon een algemene foutmelding
echo "Gebruikersnaam of wachtwoord klopt niet.";
exit;
}
?>