Bin da gestern drüber gestolpert:
bcrypt
password_hash()
password_verify()
password_needs_rehash()
Zum Thema "Salt":
Falls sich jemand fragt,
"warum denn den Salt mit speichern. Und was, wenn die db gehackt wird? Dann hat der Angreifer ja alle zugehörigen Salts?!"
>>The only requirement for a salt is for it to be globally unique. It does not have to be kept secret. password_hash() takes care of it for you. Don't do anything fancy and stupid.
Einfache Übersetzung:
Das wichtigste ist, dass das Salt einzigartig ist. Es muss nicht geheim sein.
password_hash() kümmert sich um das Salt. Es macht also keinen Sinn sich eine "Beste-Salt-function-ever" zu basteln.
heißt: der Funktion password_hash() kein eigenen Salt mitgeben
siehe auch: php.net: "Caution It is strongly recommended that you do not generate your own salt for this function."
&
>>Sehr gute Erklärung - also lesen!
Hier mal eine kurze Zusammenfassung (ohne Übersetzung =)
- The reason we use salts is to stop precomputation attacks, such as rainbow tables.
- So, we use a salt. A salt is a random unique token stored with each password.
- nobody has rainbow tables that include that hash.
- The goal is to force the attacker to have to crack the hashes once he gets the database, instead of being able to just look them all up in a rainbow table.
- One other idea to consider is a pepper. A pepper is a second salt which is constant between individual passwords, but not stored in the database. (KDF(password + pepper, salt))
- So, how do we prevent brute-force attacks now?
- A more solid approach is to use a key derivation function with a work factor. These functions take a password, a salt and a work factor. The work factor is a way to scale the speed of the algorithm against your hardware and security requirements:
hash = KDF(password, salt, workFactor) (siehe password_hash() "cost"-Faktor)
Hier ein einfaches Bsp:
/*(simuliert) formular daten bei registrierung:*/
$_POST['uname'] = 'cottton';
$_POST['pw'] = '12345678';
/*fester "geheimer" wert ("pepper")*/
// kann zB in einer config.php als constant definiert werden: define('PEPPER', 'bisschenPfeffer?') wobei schon etwas konstantes und schwer zu erratenes genutzt werden sollte
$pepper = 'bisschenPfeffer?';
/*hash:*/
// wird erst erstellt, wenn alle benötigten benutzereingaben vorhanden sind
$hash = password_hash($_POST['pw'] . $pepper, PASSWORD_BCRYPT, array('cost' => 12));
# ^= $2y$12$sWGjyoZ9xDwuGLou6fvije3q3T5SV1cGbgS.wCKsWicdTXK9czeV6
/*speichere nutzerdaten in db:*/
// hier testweise nur ein var_dump !
var_dump($hash);
/*(simuliert) formular daten bei anmeldung:*/
$_POST['uname'] = 'cottton';
$_POST['pw'] = '12345678';
/*(simuliert) daten aus db laden:*/
$db_daten = array(
'uname' => 'cottton',
'pw' => '$2y$12$sWGjyoZ9xDwuGLou6fvije3q3T5SV1cGbgS.wCKsWicdTXK9czeV6',
);
/*prüfen ob hash aus db zu passwort aus anmeldung passt*/
// dabei muss PEPPER an die gleiche stelle angehangen werden, wie es beim erstellen des hashes geschehen ist
$valid = password_verify($_POST['pw'] . $pepper, $db_daten['pw']);
var_dump(
$valid
? 'Password is valid!'
: 'Invalid password!'
);
Alles anzeigen
Zusammenfassung:
Wir haben also erreicht, dass der Angreifer:
- in die db eindringen muss (klar) um den Passwort-hash und Salt zu bekommen
- aber auch zugriff auf das Pfeffer ($pepper) haben muss - sich somit also auch Zugriff auf die Files verschaffen muss
- gezwungen ist den Passwort-hash zurück zu Rechnen, anstatt in "rainbow tables" danach zu suchen.
Das Ganze nun noch abgerundet, indem man Prepared Statements nutzt (siehe php.net "PDO"), um eine SQL-injection zu verhindern (wenn richtig angewandt!).
EDIT: 05.06.2016
Es ist wichtig die Funktion password_verify zur Prüfung des Passwortes zu nutzen
wenn sich User sich einloggt nicht if ($hash === $hashLogin), sondern wirklich password_verify() nutzen
Warum: um Timing_attacks zu unterbinden.
Siehe http://stackoverflow.com/a/29489833/3411766
Zitat von http://stackoverflow.com/a/29489833/3411766
The answer is YES it uses length-constant time comparison.
This is an excerpt of php's password_verify function
Code/* We're using this method instead of == in order to provide * resistance towards timing attacks. This is a constant time * equality check that will always check every byte of both * values. */ for (i = 0; i < hash_len; i++) { status |= (ret->val[i] ^ hash[i]); }
You can have a look at the full source code at https://github.com/php/php-src…r/ext/standard/password.c
"Man kanns auch übertreiben ..." ? - Nein. Man kann es auch gleich richtig machen =)