Kennwörter nur als Hash-Wert speichern

Kennwörter dürfen natürlich nicht im Klartext gespeichert werden, da die Anwendung oder Datenbank kompromittiert werden können. Daher werden für die Kennwort-Validierung Häshing-Verfahren verwendet.

Hashing stellt eine unidirektionale Funktion dar (d.h. es ist nicht möglich, einen Hash zu "entschlüsseln" und so den ursprünglichen Klartextwert zu erhalten). Selbst wenn ein Angreifer das gehashte Kennwort in Erfahrung bringt, kann er es so nicht in das Kennwortfeld einer Anwendung eingeben und sich als das Opfer anmelden.

Kennwort => Hash-Wert

Keine Verschlüsselung von Kennwörtern verwenden

Die Verschlüsselung ist im Gegensatz zum Hashing eine bidirektionale Funktion, was bedeutet, dass der ursprüngliche Klartext abgerufen werden kann. Gelingt Angreifern die Entschlüsselung, sind sofort alle Kennwörter kompromittiert.

Kennwort <=> Verschlüsseltes Kennwort

Die Verschlüsselung eignet sich daher nur für die Speicherung von Daten wie der Adresse eines Benutzers, wenn diese Daten z.B. im Klartext im Profil des Benutzers angezeigt werden sollen.

Hashes "knacken"

Zwar es nicht möglich ist, Kennwort-Hashes zu "entschlüsseln", aber unter bestimmten Umständen kann man sie "knacken".

Vorgehen nach Erlangung eines gespeicherten Hash-Werts

  1. Es wird ein Kennwort gewählt (z.B. password1!).
  2. Der Hash-Wert wird berechnet.
  3. Vergleichen der Hash-Werte. Bei Übereinstimmung, ist der Hash "geknackt" und das Kennwort im Klartext bekannt.

Dieser Vorgang wird für sehr viele mögliche Kennworte wiederholt. Dabei werden

  • Listen von Kennwörtern ausprobiert, die von anderen kompromittierten Websites stammen,
  • Brute Force-Angriffe gemacht (Ausprobieren aller möglichen Kennworte),
  • Wörterbücher oder Wortlisten mit gängigen Passwörtern verwendet.

Trotz der vielen Möglichkeiten sind die Kosten dabei mit Hochgeschwindigkeitshardware (z. B. Grafikprozessoren) und Cloud-Diensten mit vielen gemieteten Servern relativ gering, vor allem, wenn keine bewährten Verfahren für das Hashing befolgt werden.


Moderne Verfahren

Salting

Ein "salt" (dt. Salz) ist eine eindeutige und zufällig generierte Zeichenfolge, die zu jedem Kennwort als ein Teil des Hashing-Prozesses hinzugenommen wird:

Kennwort + individualles Salt => individueller Hash-Wert

Das "salt" wird gemeinsam mit dem Hash-Wert in der Datenbank gespeichert und würde zwar gemeinsam mit dem Hash-Wert in die Hände des Angreifers gelangen, aber da es für jedes Kennwort einmalig generiert wird, müssen die Hashes nacheinander mit dem jeweiligen Salt geknackt werden. Es ist nicht mehr möglich einen berechneten Hash sofort mit allen gespeicherten Hashes zu vergleichen. Dadurch wird das Knacken einer größeren Anzahl von Hashes erheblich erschwert, da die benötigte Zeit direkt proportional zur Anzahl der Hashes wächst.

Da sich die Salts voneinander unterscheiden, sind auch schwache (z. B. häufig verwendete, wiederverwendete) Kennwörter etwas besser geschützt, da jeweils unterschiedliche Hashes erstellt werden.

Salting schützt außerdem vor der einfachen Anwendung von sogenannten Rainbow-Tabellen. Sie sind leicht im Internet zu finden und enthalten gängige Kennworte und die dazugehörigen Hash-Werte verschiedener Hashing-Verfahren ohne Salt. Salting bedeutet, dass Angreifer jeweils eine Rainbow-Tabelle für jeden einzelnen Benutzer und dessen Salt neu berechnen müssen.

Peppering

Ein "Pepper" (dt. Pfeffer) ist eine weitere geheime Zeichenkette und kann zusätzlich zum Salting verwendet werden, um eine weitere Schutzebene zu schaffen. Damit soll verhindert werden, dass ein Angreifer einen der Hashes knacken kann, wenn er nur Zugriff auf die Datenbank hat, denn der Pepper wird dann natürlich nicht in der Datenbank sondern im Quellcode festgelegt.

Ein gebräuchliches Peppering-Verfahren besteht darin, die Kennwörter zuerst wie üblich mit Salt zu hashen und dann diese Hashes zusätzlich mit einem gemeinsamen Pepper als Key symmetrisch zu verschlüsseln. Die verschlüsselten Hashes werden in der Datenbank gespeichert.

                                                                   ein gemeinsamer Pepper
v
Kennwort + individualles Salt => individueller Hash-Wert <=> verschlüsselter individueller Hash-Wert

Wie bei anderen kryptografischen Schlüsseln sollte eine Strategie zur Rotation von Peppers in Betracht gezogen werden.

(Die Verwendung von Pepper wird durchaus kontrovers diskutiert, z.B. hier: https://stackoverflow.com/questions/16891729/best-practices-salting-peppering-passwords.)


Work Factors

Der Hashing-Algorithmus darf nicht zu schnell sein, um das Knacken der Hashes zu erschweren. Eine Verlangsamung wird mit dem Work Factor (dt. Arbeitsfaktor) erreicht. Aus diesem Grund gelten Algorithmen wie z.B. md5, sha-1 sha256, etc.als veraltet, denn sie sind auf Schnelligkeit ausgelegt und lassen sich nicht verlangsamen.

Der Work Factor ist im Wesentlichen die Anzahl der Iterationen des Hash-Algorithmus, die für jedes Kennwort durchgeführt werden (normalerweise 2Work Factor). Der Zweck des Arbeitsfaktors besteht darin, die Berechnung des Hashwerts rechenintensiver zu machen, was wiederum die Geschwindigkeit verringert und/oder die Kosten erhöht, mit denen ein Angreifer versuchen kann, den Hashwert zu knacken. (Der Arbeitsfaktor wird normalerweise in der Hash-Ausgabe gespeichert.)

Bei der Wahl eines Arbeitsfaktors muss ein Gleichgewicht zwischen Sicherheit und Leistung gefunden werden. Je höher der Arbeitsfaktor ist, desto schwieriger ist es für einen Angreifer, den Hash zu knacken, aber desto langsamer ist auch der Prozess der Überprüfung eines Anmeldeversuchs. Ein zu hoher Arbeitsfaktor kann die Leistung der Anwendung beeinträchtigen und könnte von einem Angreifer für einen Denial-of-Service-Angriff genutzt werden, indem er eine große Anzahl von Anmeldeversuchen unternimmt, um die CPU des Servers auszulasten.

Es gibt keine goldene Regel für den idealen Arbeitsfaktor - er hängt von der Leistung des Servers und der Anzahl der Benutzer in der Anwendung ab. Um den optimalen Arbeitsfaktor zu ermitteln, sind Experimente mit dem/den von der Anwendung verwendeten ServerNo erforderlich. In der Regel sollte die Berechnung eines Hashes weniger als eine Sekunde dauern.

Ein wesentlicher Vorteil eines Arbeitsfaktors ist, dass er im Laufe der Zeit erhöht werden kann, wenn die Hardware leistungsfähiger und billiger wird.

Die gängigste Methode, den Arbeitsfaktor zu erhöhen, besteht darin, zu warten, bis sich der Benutzer das nächste Mal authentifiziert, und dann sein Kennwort mit dem neuen Arbeitsfaktor erneut zu hashen. Dies bedeutet, dass verschiedene Hashes unterschiedliche Arbeitsfaktoren haben und dazu führen können, dass Hashes nie aktualisiert werden, wenn der Benutzer sich nicht wieder bei der Anwendung anmeldet. Je nach Anwendung kann es sinnvoll sein, die älteren Passwort-Hashes zu entfernen und die Benutzer aufzufordern, ihre Passwörter bei der nächsten Anmeldung neu zu setzen, um die Speicherung älterer und weniger sicherer Hashes zu vermeiden.


Hashing-Algorithmen

Es gibt eine Reihe moderne Hashíng-Algorithmen, die speziell für die sichere Speicherung von Kennwörtern entwickelt wurden. Im Folgenden wird hier beispielhaft nur das im Code verwendete Argon2 als der Gewinner des Passwort-Hashing-Wettbewerbs 2015 erwähnt.

Argon2id

Es gibt drei verschiedene Versionen des Algorithmus, wobei die Argon2id-Variante verwendet werden sollte, da sie einen ausgewogenen Ansatz bietet, um sowohl Seitenkanal- als auch GPU-basierte Angriffe abzuwehren.

Anstatt eines einfachen Work Factors wie bei anderen Algorithmen, hat Argon2id drei verschiedene Parameter, die konfiguriert werden können. Argon2id sollte eine der folgenden Konfigurationseinstellungen als Basis-Minimum verwenden, die die minimale Speichergröße (m - memory cost in ki), die minimale Anzahl von Iterationen (t - time cost) und den Grad der Parallelität (p - parallelism degree = numbers of parallel threads) umfasst:

    m=47104 (46 MiB), t=1, p=1 (nicht mit Argon2i verwenden)
    m=19456 (19 MiB), t=2, p=1 (Nicht mit Argon2i verwenden)
    m=12288 (12 MiB), t=3, p=1
    m=9216 (9 MiB), t=4, p=1
    m=7168 (7 MiB), t=5, p=1

Diese Konfigurationseinstellungen sind in Bezug auf den Schutz, den sie bieten, gleichwertig. Der einzige Unterschied besteht in einem Kompromiss zwischen CPU- und RAM-Nutzung.

Die Installation erfolgt mit

pip install argon2_cffi

Ein Beispielprogramm erläutert die Standardbenutzung:

from argon2 import PasswordHasher
ph = PasswordHasher()

hash1 = ph.hash("testPwd")
print(hash1) 
print(ph.verify(hash1, "testPwd"))

hash2 = ph.hash("testPwd")
print(hash2)  
print(ph.verify(hash2, "testPwd"))

print(ph.verify(hash2, "testWrongPwd"))


Die Ausgabe ist jedesmal anders, weil argon2 das Salt selbständig neu erzeugt:

$argon2id$v=19$m=65536,t=3,p=4$UcqpaF5L6RciF+MrH6u4pQ$CE1xFLTARDevWVj1SD6fqCgy3Ier+SFsNh7sf1N74IA
True
$argon2id$v=19$m=65536,t=3,p=4$1Rq0eOvMvWdzYekjiCmsVQ$1sdajDrI9IyhFZktRxvMn2megpvvHh20cCOHnet88rg
True
Traceback (most recent call last):
  ...
argon2.exceptions.VerifyMismatchError: The password does not match the supplied hash

Mit zurückgegeben werden dabei Typ des verwendeten Algorithmus, die Version, Memory Costs, Time Costs, degree of parallelism, Salt und Hash-Wert. Kann das Kennwort nicht verifiziert werden, wird eine entsprechende Exception geworfen.



Quelle und weitere oben nicht beschriebene Verfahren:

https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html   (30.01.2023)

Weitere Quellen:

https://pypi.org/project/argon2-cffi/   (30.01.2023)
https://argon2-cffi.readthedocs.io/en/stable/   (30.01.2023)

Online Hashing Tools:

hashgenerator.de - generate md4 md5 sha1 sha256 sha512 ripemd and whirlpool hashes on the fly
Hash decoder and calculator (md5hashing.net)

Last modified: Wednesday, 1 February 2023, 12:16 PM