Sichere Passwort-Hashes für Anwendungen

Fast jeder Entwickler hat bestimmt schonmal eine Benutzertabelle angelegt und Kennwörter als Hash gespeichert. Doch wie können wir diese Hashes möglichst sicher erstellen? Dieser Artikel wird die Thematik etwas näher erläutern und ein Codebeispiel liefern.
Klären wir zuerst die Frage, was das Wort „sicher“ bei einer Verschlüsselung heißt. Gehen wir zuerst davon aus, dass nichts zu 100% sicher ist. Denn ältere Hash- oder Verschlüsselungsalgorithmen wurden mit der Zeit geknackt und als unsicher eingestuft. Die Computer werden auch in Zukunft immer Leistungsfähiger werden. Wenn eine Person oder Unternehmen über ein großes Budget verfügt, ist auch das Mieten oder Aufbauen einer großen Serverfarm möglich. Durch horizontale Skalierung ist die Leistungsfähigkeit prinzipiell unendlich groß.
„Sicherheit“ bedeutet eher, dass jemand sehr lange zum Entschlüsseln einer Information braucht. Im besten Falle ist die benötigte Zeit unendlich groß oder (etwas realitätsnäher formuliert) eine Information ist nicht mehr relevant. Sei es, weil ein Ereignis bereits eingetreten ist oder die Informationen keinen Schaden mehr anrichten können. Ich denke da spontan an E-Mails, wo beispielsweise ein gemeinsames Treffen organisiert oder die Finanzdaten eines Unternehmens ausgetauscht werden. Je mehr Zeit die Entschlüsselung braucht, desto sicherer ist die Verschlüsselung.
Anfang Juni kam ein sehr interessanter Artikel von Jeff Atwood heraus, welcher die Absicherung der Benutzerinformationen von Discourse näher beleuchtet und die Sicherheit verschiedener Hashes vergleicht (Hacker, Hack Thyself ).
Die Dokumentation von Microsoft bezüglich Passwort Hashes in ASP.NET-Core verwendet den PBKDF2 Algorithmus. Der Salt-Wert sollte für jeden Benutzer individuell angelegt werden. Das erschwert die Erstellung bzw. Verwendung von Rainbow Tables .
Das Codebeispiel von Microsoft sieht folgendermaßen aus:
// generate a 128-bit salt using a secure PRNG
byte[] salt = new byte[128 / 8];
using (var rng = RandomNumberGenerator.Create()) {
rng.GetBytes(salt);
}
// derive a 256-bit subkey (use HMACSHA1 with 10,000 iterations)
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
password: password,
salt: salt,
prf: KeyDerivationPrf.HMACSHA1,
iterationCount: 10000,
numBytesRequested: 256 / 8));
Der RandomNumberGenerator erstellt einen zufälligen Wert. Dieser Wert wird in der darauf folgenden Hashfunktion verwendet.
Wichtig ist nur, dass beide Werte (Passworthash und Salt) gemeinsam gespeichert werden. Ein Klartext-Kennwort muss zur Validierung mit dem selben Salt wie der Hash verschlüsselt werden. Sind der gespeicherte und berechnete Hash identisch, ist das Kennwort gültig.
Was sind eure bisherigen Erfahrungen im Bereich Hashing und Benutzerverwaltung? Ich freue mich über eure Kommentare 🙂