Die nativ ab PHP 5.5 implementierten Funktionen password_hash und hash_pbkdf2 hatte ich schon erwähnt.
Na, wenn Du schon hash_pbkdf2 erwähnst, dann solltest Du aber auch erwähnen, dass man keinen statischen (für alle gehashed zu speichernden Passwörter gleichen) salt verwenden sollte.
Damit wären wir dann aber wieder - abgesehen vom verwendeten Hash-Algorithmus - bei meiner Beispiel-Implementation, bei der JEDES Passwort einen EIGENEN, ZUFÄLLIGEN Salt erhält, der mit dem hash zusammen gespeichert wird. Hier jetzt also das BEISPIEL mit hash() oder pbkdf2().
<?php
# Warnung bei einer Umstellung auf PHP 5.5. werden alle Passwörter ungültig!
# Und das ist ein Beispiel...
if (! defined('PASSWD_ITERATIONS')) {
define('PASSWD_ITERATIONS', 1000000);
}
if (! defined('PASSWD_SALT_LENGHT')) {
define('PASSWD_SALT_LENGHT', 32);
}
# ggf. anpassen, gültige Werte aus hash_algos(); Alternativen wären: sha512,tiger192,4, gost, haval256,5
# http://en.wikipedia.org/wiki/List_of_hash_functions
# http://en.wikipedia.org/wiki/Comparison_of_cryptographic_hash_functions
if (! defined('PASSWD_ALGO')) {
define('PASSWD_ALGO', 'sha512');
}
# für PHP > 5.5 ggf. anpassen, gültige werte aus hash_algos()
if (! defined('PASSWD_ALGO_BBKDF2')) {
define('PASSWD_ALGO_BBKDF2', 'sha512');
}
#### TEST #####
#/* Zum Testen am anfang dieser Zeile Raute (#) vor SlashAsterix(/*) setzen -> #/*
$test = craeteSaltedHash('Hallo');
print "gehaschtes Passwort: $test\n";
echo "(", strlen($test), " Zeichen)\n";
echo "Algos: ", implode( ', ', hash_algos() ), "\n";
if (checkSaltedHash('Hallo' , $test) ) {
echo "Passt!\n";
} else {
echo "Nö!!\n";
}
#*/
#### /TEST #####
function craeteSaltedHash($passwd) {
if ('' == $passwd) { # oder bessere Prüfung!
return false;
}
$salt='';
for ($i=0; $i<PASSWD_SALT_LENGHT; $i++) {
$salt .= chr( rand( 33, 125 ) ); # ascii von '!' bis '}' # OHNE '~'!
}
return ( $salt.'~'.my_hash($passwd, $salt) );
}
function checkSaltedHash($passwd, $saved_string) {
list($salt,$rest)=explode('~', $saved_string, 2);
# print "salt: '$salt'\n"; #### DEBUG
# print "rest: '$rest'\n"; #### DEBUG
if ( my_hash($passwd, $salt) == $rest ) {
return true;
} else {
return false;
}
}
function my_hash($passwd, $salt) {
if ( function_exists('hash_pbkdf2') ) {
echo "funktion 'hash_pbkdf2'wird benutzt:\n";
return hash_pbkdf2(PASSWD_ALGO_BBKDF2 , $passwd , $salt , PASSWD_ITERATIONS);
} else { # PHP-Version < 5.5
$passwd_original=$passwd;
for($i=0; $i<PASSWD_ITERATIONS; $i++) {
if (0==$i%2) {
$passwd=hash(PASSWD_ALGO, $salt.$passwd);
} else {
$passwd=hash(PASSWD_ALGO, $passwd_original.$passwd);
}
}
return $passwd;
}
}
?>
Wenn man von Krypto wenig Ahnung hat, sollte man besser Bibliotheken und Funktionen von Leuten verwenden, die wissen, was sie tun.
Wenn man aber von Kryptographie so fürchterlich viel Ahnung hat, dass man anderen "so locker vom hohen Hocker" Ahnungslosigkeit vorwerfen kann, dann sollte man die sinntragende Verwendung der selbst empfohlenen Funktionen auch wenigstens kurz erklären. Die Dokumentation sagt nämlich wenig zur sinnvollen Implementierung - also zu der Frage, warum wie viele Iterationen zu verwenden sind und wie ein salt denn nun sinnvoll verwendet wird... Du knallst den Lesern ein paar Begriffe und Funktionen um die Ohren und was dann rauskommt ist mit brauchbarer Wahrscheinlichkeit gleich mal um Größenordnungen unsicherer als mein Beispiel, welches ich mit "etwas wie das hier" ganz genau richtig beschrieben habe.
Zum Beispiel dürfte bei Befolgen Deines Vorschlages ein $hash=hash_pbkdf2 ('md5' , $passwd , '' , 1)
nämlich nur wenig bringen, wäre sogar kontraproduktiv, weil der Verwender jetzt irrt - "He! Ich habe doch die empfohlene Funktion hash_pbkdf2() verwendet! Das ist also sicher!"
Daneben gibt es noch ein Problem. Die Funktionen sind so ziemlich schwarze Boxen. Was, wenn ich eines Tages von PHP auf eine andere Sprache umstellen will oder muss? In der es hash_pbkdf2() nicht gibt? - Richtig. Dann darf ich mir den Quelltext von PHP anschauen und versuchen herauszubekommen, was diese Funktions-Mäntel (das genau sind diese) eigentlich genau treiben und das nachbauen. Oder meine Benutzer dazu anhalten, über die "Passwort vergessen - Funktion" neue Passwörter einzugeben... - das wäre übrigens auch das, was passiert, wenn das obige Beipspiel verwendet und dann irgendwann z.B. von PHP 5.3 auf PHP 5.5 umgestellt wird. Im aktuellen Ubuntu-Server 12.04 (LTS) ist die PHP-Version (aus dem REPO) übrigens aktuell 5.4.9.