Verwendung von Passwort-Hash mit unzureichendem Rechenaufwand
Beschreibung
Die Verwendung von Passwort-Hash mit unzureichendem Rechenaufwand tritt auf, wenn Software einen Passwort-Hashing-Algorithmus verwendet, der keine signifikanten Rechenressourcen zur Berechnung erfordert. Schnelle Hash-Funktionen wie MD5, SHA-1 oder sogar SHA-256 ohne Key-Stretching können milliardenfach pro Sekunde auf modernen GPUs berechnet werden. Dies ermöglicht es Angreifern, Brute-Force-Angriffe effizient durchzuführen und riesige Mengen an Passwortkombinationen in angemessener Zeit zu testen.
Risiko
Moderne GPUs können Milliarden von MD5- oder SHA-256-Hashes pro Sekunde berechnen. Eine mit schnellen Algorithmen gehashte Passwortdatenbank kann offline ohne Rate-Limiting angegriffen werden. Der LinkedIn-Breach von 2012 zeigte, wie schnell ungesalzene SHA-1-Hashes geknackt werden könnten. Selbst mit Salts ermöglichen schnelle Algorithmen systematische Brute-Force-Angriffe. Passwort-Cracking-Hardware und Cloud-Dienste machen diese Angriffe für jeden zugänglich. Kritische Anwendungen, die schwaches Hashing verwenden, sind bei einem Breach unvermeidlich einer Kompromittierung von Anmeldedaten ausgesetzt.
Lösung
Verwenden Sie Passwort-Hashing-Algorithmen, die speziell dafür entwickelt wurden, rechenintensiv zu sein: bcrypt, scrypt oder Argon2. Konfigurieren Sie Work-Faktoren angemessen - der Hash sollte mindestens 100ms zur Berechnung benötigen. Erhöhen Sie die Work-Faktoren im Laufe der Zeit, wenn die Hardware besser wird. Argon2 (Gewinner des Password Hashing Competition) ist die aktuelle Best Practice. Verwenden Sie speicherintensive Funktionen, die GPU/ASIC-Beschleunigung widerstehen. Implementieren Sie automatisches Rehashing bei der Benutzerauthentifizierung, um alte Hashes zu aktualisieren.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Passwort-Cracking Schnelle Hash-Funktionen ermöglichen Milliarden von Passwort-Vermutungen pro Sekunde. |
| Authentifizierung | Bereich: Kompromittierung von Anmeldedaten Geknackte Passwörter führen zur Übernahme von Konten. |
| Compliance | Bereich: Regulatorische Verstöße Viele Vorschriften erfordern starke Passwortspeicherung (DSGVO, PCI-DSS). |
Beispielcode + Lösungscode
Anfälliger Code
# ANFÄLLIG: Schnelle Hash-Funktion (SHA-256)
import hashlib
import os
def hash_password_vulnerable(password):
# SHA-256 ist schnell - Milliarden pro Sekunde auf GPU!
salt = os.urandom(16)
hash_value = hashlib.sha256(salt + password.encode()).hexdigest()
return f"{salt.hex()}:{hash_value}"
# ANFÄLLIG: Einzelne Iteration
def hash_password_single_iteration(password, salt):
return hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 1)
# ANFÄLLIG: Niedrige Iterationszahl
def hash_password_low_iterations(password):
salt = os.urandom(16)
# 1000 Iterationen sind viel zu wenig!
key = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 1000)
return f"{salt.hex()}:{key.hex()}"
# ANFÄLLIG: MD5 - extrem schnell
def hash_password_md5(password):
return hashlib.md5(password.encode()).hexdigest()
// ANFÄLLIG: Schnelle Hash-Funktion
import java.security.MessageDigest;
import java.util.Base64;
public class VulnerablePasswordHash {
// SHA-256 ohne Key-Stretching - viel zu schnell!
public String hashPassword(String password, byte[] salt) throws Exception {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(salt);
byte[] hash = md.digest(password.getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(hash);
}
// ANFÄLLIG: Niedrige PBKDF2-Iterationen
public String hashPasswordWeakPBKDF2(String password) throws Exception {
byte[] salt = new byte[16];
new SecureRandom().nextBytes(salt);
// 1000 Iterationen sind unzureichend!
PBEKeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
1000, // ZU NIEDRIG!
256
);
SecretKeyFactory factory =
SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
byte[] hash = factory.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(salt) + ":" +
Base64.getEncoder().encodeToString(hash);
}
}
Korrigierter Code
# SICHER: Verwendung von bcrypt mit angemessenem Work-Faktor
import bcrypt
def hash_password_bcrypt(password):
# Work-Faktor 12 = 2^12 Iterationen
# Anpassen basierend auf Ihren Performance-Anforderungen
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password.encode(), salt)
return hashed.decode()
def verify_password_bcrypt(password, stored_hash):
return bcrypt.checkpw(password.encode(), stored_hash.encode())
# SICHER: Verwendung von Argon2 (empfohlen)
from argon2 import PasswordHasher, Type
from argon2.exceptions import VerifyMismatchError
# Argon2id ist die empfohlene Variante
ph = PasswordHasher(
time_cost=3, # Anzahl der Iterationen
memory_cost=65536, # 64 MB Speicher
parallelism=4, # 4 parallele Threads
hash_len=32, # Ausgabelange
salt_len=16, # Salt-Länge
type=Type.ID # Argon2id
)
def hash_password_argon2(password):
return ph.hash(password)
def verify_password_argon2(password, stored_hash):
try:
# Verify prüft auch, ob Rehashing erforderlich ist
ph.verify(stored_hash, password)
# Prüfen, ob Parameter aktualisiert werden müssen
if ph.check_needs_rehash(stored_hash):
return True, hash_password_argon2(password) # Neuen Hash zurückgeben
return True, None
except VerifyMismatchError:
return False, None
# SICHER: PBKDF2 mit hoher Iterationszahl
import hashlib
import os
# OWASP 2023 Empfehlungen für PBKDF2
PBKDF2_ITERATIONS = 600000 # Für SHA-256
def hash_password_pbkdf2(password):
salt = os.urandom(32) # 32 Bytes = 256 Bits
key = hashlib.pbkdf2_hmac(
'sha256',
password.encode(),
salt,
PBKDF2_ITERATIONS,
dklen=32
)
# Iterationszahl für Zukunftssicherheit speichern
return f"pbkdf2:sha256:{PBKDF2_ITERATIONS}:{salt.hex()}:{key.hex()}"
// SICHER: Java Passwort-Hashing mit bcrypt
import org.mindrot.jbcrypt.BCrypt;
public class SecurePasswordHash {
// Work-Faktor 12 ist ein guter Ausgangspunkt
// Mit der Zeit erhöhen, wenn Hardware besser wird
private static final int WORK_FACTOR = 12;
public String hashPassword(String password) {
return BCrypt.hashpw(password, BCrypt.gensalt(WORK_FACTOR));
}
public boolean verifyPassword(String password, String storedHash) {
return BCrypt.checkpw(password, storedHash);
}
// Prüfen, ob Rehashing erforderlich ist (Work-Faktor erhöht)
public boolean needsRehash(String storedHash) {
// Work-Faktor aus bcrypt-Hash extrahieren
String[] parts = storedHash.split("\\$");
if (parts.length >= 4) {
int currentWorkFactor = Integer.parseInt(parts[2]);
return currentWorkFactor < WORK_FACTOR;
}
return true;
}
}
// SICHER: Verwendung von Argon2 in Java
import de.mkammerer.argon2.Argon2;
import de.mkammerer.argon2.Argon2Factory;
public class Argon2PasswordHash {
private final Argon2 argon2;
public Argon2PasswordHash() {
// Argon2id verwenden (empfohlen)
this.argon2 = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id);
}
public String hashPassword(String password) {
// Iterationen, Speicher (KB), Parallelismus
return argon2.hash(
3, // Iterationen
65536, // 64 MB Speicher
4, // Parallelismus
password.toCharArray()
);
}
public boolean verifyPassword(String password, String storedHash) {
try {
return argon2.verify(storedHash, password.toCharArray());
} finally {
// Passwort aus Speicher löschen
argon2.wipeArray(password.toCharArray());
}
}
}
Ausgenutzt in der Praxis
RockYou-Breach (2009)
32 Millionen Passwörter, die im Klartext gespeichert wurden, wurden gestohlen, was die Bedeutung eines ordnungsgemäßen Passwort-Hashings demonstrierte. Dieser Breach lieferte Passwortlisten, die noch heute verwendet werden.
LinkedIn-Breach (2012)
6,5 Millionen SHA-1-gehashte Passwörter (ohne Salt) wurden innerhalb von Stunden mit GPU-Beschleunigung geknackt, was viele weitere im vollständigen Breach von 117 Millionen Konten betraf, der 2016 enthüllt wurde.
Ashley Madison (2015)
37 Millionen Konten wurden gehackt. Obwohl sie bcrypt verwendeten, hatten ältere Konten MD5-Hashes, die schnell geknackt wurden.
Tools zum Testen/Ausnutzen
-
Hashcat - GPU-beschleunigtes Passwort-Cracking.
-
John the Ripper - CPU/GPU-Passwort-Cracker.
-
hashID - Hash-Typen identifizieren.
-
hash-identifier - Hash-Typ-Identifikation.
CVE-Beispiele
-
CVE-2021-29447 - WordPress schwaches Passwort-Hashing.
-
CVE-2019-10790 - Unzureichende PBKDF2-Iterationen.
-
CVE-2020-5735 - MD5-Passwortspeicherung.
Referenzen
-
MITRE. "CWE-916: Use of Password Hash With Insufficient Computational Effort." https://cwe.mitre.org/data/definitions/916.html
-
OWASP. "Password Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html