PHP 5.5.15 is released

Sicheres Password Hashing

Dieser Absatz erklärt, warum Hash Functionen zu sicheren Passwortspeicherung benutzt werden, und wie dies am effektivsten bewerkstelligt werden kann.

Warum sollte ich die Passwörter der User meiner Anwendung hashen?

Passworthashing ist eine der grundlegensten Sicherheitsüberlegungen, die beim Design einer Anwendung bedacht werden müssen. Ohne Hashing können im Falle eines erfolgreichen Angriffs alle Userpasswörter in Ihrer Datenbank von einem Angreifer gestohlen und ohne Umwege in anderen Anwendungen (seien es Ihre oder die Anderer) benutzt werden. Dies ist oft der Fall, da viele User für verschiedene Dienste die gleichen Passwörter verwenden.

Wenn sie Passwörter vor dem Abspeichern in der Datenbank hashen, machen Sie es einen Angreifer schwerer die usprünglichen Passwörter herauszufinden. Sie haben jedoch weiterhin die Möglichkeit eingebene Userpasswörter zu hashen und die Hashwerte zu vergleichen um herrauszufinden ob der User das richtige Passwort eingegeben hat.

Es ist jedoch wichtig zu wissen, dass Hashen nur das Auslesen der Passwörter aus dem Datenspeicher (z.B.: Datenbank oder Datei) erschwert. Sollte es einen Angreifer gelingen Code in Ihre Anwendung einzuschleußen, so kann er die Passwörter der User evtl. Auslesen bevor dieses gehasht werden.

Warum sind verbreitete Hashfunktionen wie md5() und sha1() nicht für die Speicherung von Passwörtern geeignet?

Hashalgorithmen wie MD5, SHA1 und SHA256 sind auf Geschwindigkeit und Effizienz optimiert. Mit modernen Techniken und leistungsstarker Hardware ist es aber trivial geworden diese Hashalgorithmen mit "brute force" Attacken anzugreifen.

Weil moderne Computer diese Hashalgorithmen leicht brechen können, werden diese von Sicherheitsspezialisten nicht für die Speicherung von Passwörtern empfohlen.

Wie sollte ich meine Passwörter hashen, wenn die üblichen Hashfunctionen unbrauchbar sind?

Wenn es um das Hashen von Passwörtern geht müssen zwei wichtige Sachen bedacht werden: Der Berechnungsaufwand und das Salt. Wenn länger es dauert einen Hash zu berechnen wird ein Brute Force Angriff ebenso starkt verzögert. Jedoch ist zu beachten, dass der Berechnungsaufwand sich im normalen Betrieb nicht negativ auf die Performance der Anwendung auswirkt. Das Salt hingegen dient dazu die Erstellung von vorberechneten Rainbowtables zu verhindern, da durch das Salt auch gleiche Passwörter unterschiedliche Hashes erhalten.

PHP 5.5 bietet eine native Passworthashing API die sich auf eine sichere Art um das Hashen und Verifizieren von Passwörtern kümmert. Ebenso existiert eine » native PHP kompatible Bibliothek für PHP Versionen ab 5.3.7.

Eine andere Option ist die crypt() Funktion, die mehrere Hashalgorithmen untersützt. Diese existiert ab Version 5.3.7. Wenn diese Funktion bentutz wird, ist es garantiert, dass der Hashalgorithmus untersützt wird, da PHP native Implementierung für die Algorithmen besitzt, die benutzt werden, wenn von dem Betriebsystem nicht angebotene Algorithmen benutzt werden.

Der empfohlene Algorithmus für das Hashen von Passwörtern ist Blowfish, welcher auf als Standardalgorithmus von der Passwort Hashing API benutzt wird, da dieser einen signifikant höheren Berechnungsaufwand als MD5 oder SHA1 vorraussetzt und trotzdem weiterhin skalierbar ist.

Bitte beachten Sie: Wenn sie crypt() benutzen um Passwörter zu verifzieren müssen sie dafür sorgen Timing Attackten zu verhindern. Dies kann durch Stringvergleiche mit konstanter Laufzeit gesehen. Jedoch bieten die PHP Vergleichsoperatoren == und === keine konstante Laufzeit. Da jedoch password_verify() eine konstante Laufzeit besitzt ist es stark empfohlen die native Passwort Hashing API zu benutzen, wenn dies möglich ist.

Was ist ein Salt?

Ein kryptografisches salt sind Daten, die während des Hashens an das Passwort angefügt werden um die Möglichkeit des Angriffs über Rainbowtables zu behindern. Rainbowtables sind bereits vorberechnete Paare (Schlüssel, Hash), die benutzt werden können um für einen gegeben Hash das vorberechnete Passwort nachzuschlagen.

In einfachen Worte: Ein Hash sind zusätzliche Daten, die es erschweren das Passwort zu knacken. Es gibt Onlinedienste bei denen man für einen gegebenen Hash das dazugehörige Passwort herrausfinden kann. Die Benutzung eines Salts macht es unpraktisch bis unmöglich das Passwort durch die Benutzung dieser Dienste zu finden.

Die Funktion password_hash() generiert ein zufälliges Salt sollte beim Aufruf der Funktion keines übergeben werden. Dies ist generell der einfachste und sicherste Ansatz.

wie speichere ich meine Salts?

Die Funktionen password_hash() oder crypt() geben den Salt als einen Teil des generierten Hashs zurück. Dieser Wert sollte unverändert absgepeichert werden, da er Informationen über den Hashalgorithmus enthält und direkt an die Funktion password_verify() oder crypt() übergeben werden kann um ein Passwort zu verifzieren.

Das folgende Diagramm zeigt das Format eines Rückgabewertes der Funktionen crypt() oder password_hash(). Wie sie sehen können enthalten die Hashes alle Informationen über Hashalgorithmus und Salt, wie sie für die spätere Verifikation von Passwörtern benötigt werden.


        Die Komponenten des Rückgabewerten von password_hash und crypt in dieser Reihenfolge:
        Der benutze Algorithmus, die Optionen des Algorithmus, das benutzte Salt und das gehashte Passwort.

add a note add a note

User Contributed Notes 6 notes

up
9
alf dot henrik at ascdevel dot com
4 months ago
I feel like I should comment some of the clams being posted as replies here.

For starters, speed IS an issue with MD5 in particular and also SHA1. I've written my own MD5 bruteforce application just for the fun of it, and using only my CPU I can easily check a hash against about 200mill. hash per second. The main reason for this speed is that you for most attempts can bypass 19 out of 64 steps in the algorithm. For longer input (> 16 characters) it won't apply, but I'm sure there's some ways around it.

If you search online you'll see people claiming to be able to check against billions of hashes per second using GPUs. I wouldn't be surprised if it's possible to reach 100 billion per second on a single computer alone these days, and it's only going to get worse. It would require a watt monster with 4 dual high-end GPUs or something, but still possible.

Here's why 100 billion per second is an issue:
Assume most passwords contain a selection of 96 characters. A password with 8 characters would then have 96^8 = 7,21389578984e+15 combinations.
With 100 billion per second it would then take 7,21389578984e+15 / 3600 = ~20 hours to figure out what it actually says. Keep in mind that you'll need to add the numbers for 1-7 characters as well. 20 hours is not a lot if you want to target a single user.

So on essence:
There's a reason why newer hash algorithms are specifically designed not to be easily implemented on GPUs.

Oh, and I can see there's someone mentioning MD5 and rainbow tables. If you read the numbers here, I hope you realize how incredibly stupid and useless rainbow tables have become in terms of MD5. Unless the input to MD5 is really huge, you're just not going to be able to compete with GPUs here. By the time a storage media is able to produce far beyond 3TB/s, the CPUs and GPUs will have reached much higher speeds.

As for SHA1, my belief is that it's about a third slower than MD5. I can't verify this myself, but it seems to be the case judging the numbers presented for MD5 and SHA1. The issue with speeds is basically very much the same here as well.

The moral here:
Please do as told. Don't every use MD5 and SHA1 for hasing passwords ever again. We all know passwords aren't going to be that long for most people, and that's a major disadvantage. Adding long salts will help for sure, but unless you want to add some hundred bytes of salt, there's going to be fast bruteforce applications out there ready to reverse engineer your passwords or your users' passwords.
up
11
fluffy at beesbuzz dot biz
2 years ago
The security issue with simple hashing (md5 et al) isn't really the speed, so much as the fact that it's idempotent; two different people with the same password will have the same hash, and so if one person's hash is brute-forced, the other one will as well.  This facilitates rainbow attacks.  Simply slowing the hash down isn't a very useful tactic for improving security.  It doesn't matter how slow and cumbersome your hash algorithm is - as soon as someone has a weak password that's in a dictionary, EVERYONE with that weak password is vulnerable.

Also, hash algorithms such as md5 are for the purpose of generating a digest and checking if two things are probably the same as each other; they are not intended to be impossible to generate a collision for.  Even if an underlying password itself requires a lot of brute forcing to determine, that doesn't mean it will be impossible to find some other bit pattern that generates the same hash in a trivial amount of time.

As such: please, please, PLEASE only use salted hashes for password storage.  There is no reason to implement your own salted hash mechanism, either, as crypt() already does an excellent job of this.
up
8
sgbeal at googlemail dot com
1 year ago
sha1 in conjunction with one or more salt values need not be as insecure as the above makes it out to be. e.g. the Fossil SCM creates an sha1 password hash based on a user's clear-text password combined with the user's name and a shared secret known only to the specific source repository for which the user is set up.
up
3
loveun1x at yahooc dot om
8 months ago
The section "Why are common hashing functions such as md5() and sha1() unsuitable for passwords?" is completely wrong. No one says MD5 and SHA-1 aren't suitable for password hashing due to their speed. (MD5 is broken for digital signatures but not yet for password hashing, but not because it is fast.) A single round of them is unsuitable, but that is not a problem with the underlying algorithms themselves.

Always use a salt, preferably a big (e.g. at least 16 characters) salt. Always run through multiple rounds of of the hashing. Most implementations today use several tens of thousands or hundreds of thousands of rounds, for example 7-zip's encrypted format uses 262,144 rounds of SHA-256.
up
-7
user at wample dot com
4 months ago
password hashing may be better with the use of "phpass"
up
-24
owlstead
11 months ago
Clearly you should by now use PBKDF2, bcrypt or scrypt to store secure "hashes" of passwords. Unfortunately the PHP crypto implementations and documentation keeps propagating insecure protocols and algorithms. Isn't there a single PHP programmer that could clean up the cryptographic functions and libraries?
To Top