Kryptografisch sichere Passwortgenerierung in Python: random vs secrets
Entropie definiert die Qualität eines Passworts nicht als Eigenschaft der Zeichenkette selbst, sondern als Merkmal des Generierungsprozesses. Die Formel H = L × log₂(N) geht von einer unabhängigen und gleichwahrscheinlichen Auswahl jedes Zeichens aus einem Alphabet der Größe N über eine Länge L aus. Für 8 Kleinbuchstaben (N=26): ~37,6 Bits. Für vollständiges ASCII (N=94): ~79 Bits.
Ein häufiges Missverständnis: Die scheinbare Zufälligkeit eines Passworts garantiert nicht seine Stärke. Zwei Passwörter wie f9A$kL2pQzX1 können dieselbe formale Entropie haben, aber je nach verwendetem Generator sehr unterschiedliche reale Sicherheit bieten.
Das random-Modul: ein deterministischer PRNG
Das random-Modul implementiert den Mersenne Twister (MT19937) mit einem internen Zustand von ~19937 Bits. Jeder Wert ist deterministisch basierend auf dem vorherigen. Der Standard-Seed wird von os.urandom() bezogen, aber die inhärenten Eigenschaften des PRNG bleiben:
- Determinismus: Ein Seed ergibt eine vorhersehbare Sequenz.
- Wiederherstellbarkeit: Der Zustand kann aus ~624 rohen 32-Bit-Ausgaben rekonstruiert werden.
- Fehlende kryptografische Sicherheit: Nicht resistent gegen Zustandsrückgewinnungsangriffe.
Beispiel für typischen Code:
import random
import string
alphabet = string.ascii_letters + string.digits
def generate_password(length=12):
return ''.join(random.choice(alphabet) for _ in range(length))
Das Passwort ist ein Ausschnitt der PRNG-Sequenz. Wenn der Zustand bekannt ist, ist die Sequenz vorhersehbar.
Das secrets-Modul: direkter Zugriff auf Systementropie
Secrets verwendet os.urandom() direkt für jeden Aufruf:
- secrets.randbelow(n) → os.urandom().
- random.SystemRandom() → os.urandom().
Es gibt keinen festen Zustand, keinen Determinismus. Jedes Byte stammt aus Hardwarequellen: Interrupts, Netzwerkrauschen, Mausbewegungen.
Der Unterschied ist entscheidend: random erzeugt eine Illusion von Sicherheit, secrets bietet sie.
Praktischer Angriff: Wiederherstellung von Seed und Zustand
Experiment mit 5 aufeinanderfolgenden Passwörtern von GigaChat (Länge 12, Python 3.9.18, Zeit 24.03.2026 11:11):
Hypothese: Seed = Server-Zeitstempel ±1 Stunde.
from datetime import datetime, timedelta
base_time = datetime(2026, 3, 24, 11, 11, 0)
window_seconds = 3600
for offset in range(-window_seconds, window_seconds):
t = base_time + timedelta(seconds=offset)
seed = int(t.timestamp())
# Initialisiere random.seed(seed) und überprüfe
Ergebnis: keine Übereinstimmungen. Die Verwendung von os.urandom() für den Seed vereitelt brute-force zeitbasierte Angriffe.
Ein echter Angriff auf MT19937 erfordert 624 rohe Werte. Passwörter sind eine verzerrte Ausgabe (Auswahl → Zeichen), insgesamt nur 60 Auswahlen für 5 Passwörter. Eine Wiederherstellung ist unmöglich.
Angriffsgrenzen:
- Erfordert rohe PRNG-Ausgaben.
- Vorhersehbarer Seed (Zeit, PID).
- Wiederholte Verwendung desselben Generators.
- Bekannter Algorithmus.
Wichtige Erkenntnisse
- Entropie ist eine Eigenschaft des Prozesses, nicht des Ergebnisses: Überprüfen Sie immer Ihren Generator.
- random (Mersenne Twister) ist anfällig für Zustandsrückgewinnung bei ausreichenden Daten.
- secrets/os.urandom ist die einzige Wahl für Passwörter, Schlüssel und Tokens.
- Die Illusion von Zufälligkeit durch random ist in der Produktion gefährlich.
- PRNG-Angriffe sind nur mit Seed-Fehlern oder Zustandslecks realistisch.
Empfehlungen für mittlere/senior Entwickler
Implementieren Sie in Ihrem Code:
import secrets
import string
alphabet = string.ascii_letters + string.digits + string.punctuation
def secure_password(length=16):
return ''.join(secrets.choice(alphabet) for _ in range(length))
Vermeiden Sie:
- Die Verwendung von random für Authentifizierung.
- Feste Seeds.
- Wiederverwendung von Generatoren.
Test: Generieren Sie 10⁶ Passwörter, prüfen Sie auf gleichmäßige Verteilung (chi²).
— Editorial Team
Noch keine Kommentare.