Layer 8Ehrlicher Tech-Support

Zufallszahlen, die keine sind: Codes lassen sich raten

Gutscheincodes, Rücksetz-Links und Losnummern aus der normalen Zufallsfunktion sind vorhersagbar. Für Sicherheit gibt es einen anderen Generator.

Aufwand:
ca. 20 Min.
Voraussetzung:
Python 3, Eine Kommandozeile, Die Stelle im Code, an der Codes erzeugt werden
Gilt für:
Python 3 (random gegen secrets), Linux (getrandom und /dev/urandom), Jede Sprache mit einem eingebauten Pseudozufallsgenerator

Das Symptom

Ein Kunde meldet, er habe einen fremden Gutschein einlösen können – er hatte nur ein bisschen an seinem eigenen Code weitergezählt. Oder in der Datenbank fällt auf, dass die Bestellkennungen einem Muster folgen, das ein aufmerksamer Mensch in zehn Minuten erkennt. Oder, der schlimmste Fall: Jemand hat einen Passwort-Rücksetz-Link für ein fremdes Konto benutzt, ohne je Zugriff auf dessen Postfach gehabt zu haben.

Im Code steht an all diesen Stellen etwas völlig Unauffälliges: die Zufallsfunktion, die jede Programmiersprache mitbringt.

Die „Layer 8“-Wahrheit

Es gibt zwei Sorten Zufall, und sie heißen fast gleich. Der eingebaute Generator liefert Zahlen, die jeden statistischen Test bestehen – und die trotzdem restlos vorhersagbar sind, weil sie aus einer festen Rechenvorschrift und einem inneren Zustand entstehen. Die Python-Dokumentation schreibt das ungewöhnlich klar hin: Der Mersenne Twister sei einer der am gründlichsten geprüften Generatoren überhaupt und, weil vollständig deterministisch, für kryptografische Zwecke vollkommen ungeeignet. In einer eigenen Warnung steht es noch einmal: nicht für Sicherheitszwecke benutzen, dafür gibt es ein anderes Modul.

Der Layer-8-Anteil liegt in der Verwechslung von „sieht zufällig aus“ mit „kann niemand erraten“. Verschärft wird sie durch einen zweiten, ebenso verbreiteten Irrtum: Man startet den Generator mit der Uhrzeit und hält den Startwert für ein Geheimnis. Ist er nicht – ein Sekundenwert hat pro Tag 86 400 Möglichkeiten, und der Erzeugungszeitpunkt steht im Zweifel als Anlagedatum direkt neben dem Code in derselben Tabelle. Wer das Zeitfenster kennt, rechnet die Codes nach, statt sie zu raten.

Die Reparatur ist unspektakulär und meist eine Zeile lang: der Generator, den die Sprache ausdrücklich für Geheimnisse anbietet, der seine Bytes vom Betriebssystem holt und keinen Startwert von außen annimmt. Zwei Dinge gehören trotzdem dazu. Erstens genügend Länge, denn ein sicher erzeugter sechsstelliger Code ist immer noch nur eine Million Möglichkeiten. Und zweitens: Alles, was mit dem alten Generator schon ausgegeben wurde, bleibt vorhersagbar, bis es jemand ungültig macht.

Die echte, verifizierte Lösung

1000 % faktenbasiert. Jeder Befehl ist real, jeder Pfad existiert. Schritt für Schritt:

  1. 1.

    Den Generator im eigenen Code identifizieren

    Suche die Stelle, an der Codes, Kennungen, Rücksetz-Links, Gutscheine oder Losnummern entstehen. Wenn dort die allgemeine Zufallsfunktion der Sprache steht, ist das der Fund. Bekannte Namen dafür sind `random` in Python, `Math.random()` in JavaScript, `rand()` in C und PHP. Keiner davon ist für Geheimnisse gedacht, und keiner behauptet das von sich.

    grep -rn "random\.\|Math\.random\|mt_rand\|rand()" --include=*.py --include=*.js .
    
  2. 2.

    Sich selbst vorführen, dass die Folge wiederholbar ist

    Der eindrucksvollste Beweis ist der kürzeste: Derselbe Startwert liefert denselben Code, jedes Mal, auf jedem Rechner. Das ist keine Panne, sondern die dokumentierte Eigenschaft eines deterministischen Generators – und genau deshalb taugt er für Simulationen, bei denen man denselben Durchlauf wiederholen will.

    python3 -c "
    import random, string
    A = string.ascii_letters + string.digits
    random.seed(42); print(''.join(random.choices(A, k=12)))
    random.seed(42); print(''.join(random.choices(A, k=12)))
    "
    # NbrnTP3fAbnF
    # NbrnTP3fAbnF
    
  3. 3.

    Den Angriff nachbauen: das Zeitfenster durchprobieren

    Wer den Generator mit der Uhrzeit startet, hat kein Geheimnis, sondern eine Rechenaufgabe verschenkt. Für jeden Sekundenwert im Verdachtsfenster wird derselbe Code erzeugt wie auf dem Server. Der Kandidat mit dem passenden Zeitstempel steht danach im Klartext da – hier die mittlere Zeile.

    python3 -c "
    import random, string
    A = string.ascii_letters + string.digits
    for k in range(1785999999, 1786000002):
        random.seed(k); print(k, ''.join(random.choices(A, k=12)))
    "
    # 1785999999 N57zjNRdjV54
    # 1786000000 tdlxaDHjEmy1
    # 1786000001 kb6wBZwNsFlG
    
  4. 4.

    Auf den Generator für Geheimnisse umstellen

    Die Umstellung ist meist eine Zeile. Das Modul `secrets` bringt fertige Funktionen für genau diese Fälle mit: eine Zeichenkette für Links und Token, eine Zahl unterhalb einer Obergrenze für Losnummern. Keinen Startwert setzen, keine eigene Zeichenauswahl basteln – die Voreinstellung ist hier die sichere.

    python3 -c "
    import secrets
    print(secrets.token_urlsafe(16))
    print(secrets.randbelow(1000000))
    "
    # G959cV50zlMd4D_VuCbGLA
    # 245886
    
  5. 5.

    Auf der Kommandozeile aus derselben Quelle schöpfen

    Wo kein Programm läuft, sondern nur ein Skript, holt man die Bytes direkt vom Betriebssystem. Dieselbe Quelle, die getrandom bedient, liegt unter Linux als Gerätedatei bereit. Damit lassen sich Anfangspasswörter oder Kennungen erzeugen, ohne einen eigenen Generator zu schreiben – jeder Aufruf liefert etwas anderes.

    head -c 12 /dev/urandom | base64
    # 8z9OsbNlDFlvVGSc
    
  6. 6.

    Die Länge nicht am Aussehen bemessen

    Ein sicherer Generator nützt nichts, wenn der Code zu kurz ist: Sechs Ziffern sind eine Million Möglichkeiten und in Minuten durchprobiert, wenn niemand mitzählt. Für Rücksetz-Links und Einladungscodes gilt deshalb: reichlich lang, und zusätzlich eine Begrenzung der Fehlversuche sowie ein Ablaufdatum. Länge ersetzt keine Sperre, und eine Sperre ersetzt keine Länge.

  7. 7.

    Alte Codes für ungültig erklären, nicht nur den Generator tauschen

    Wurden Rücksetz-Links, Sitzungskennungen oder Gutscheincodes mit dem falschen Generator erzeugt, ist der Austausch der Funktion nur die halbe Arbeit. Alles, was vorher ausgegeben wurde, bleibt vorhersagbar – noch offene Codes gehören entwertet und neu ausgestellt. Wer nur den Code ändert und die Altbestände laufen lässt, hat die Lücke dokumentiert, aber nicht geschlossen.

Häufige Fragen (FAQ)

?Die Zahlen sehen doch völlig zufällig aus. Wo ist das Problem?

Zufällig aussehen und unvorhersagbar sein sind zwei verschiedene Eigenschaften. Die Python-Dokumentation beschreibt den eingebauten Generator so: Python benutze den Mersenne Twister, dieser sei einer der am gründlichsten geprüften Zufallszahlengeneratoren überhaupt – und dann kommt der entscheidende Satz: „However, being completely deterministic, it is not suitable for all purposes, and is completely unsuitable for cryptographic purposes.“ Vollständig deterministisch heißt: Aus dem inneren Zustand folgt die gesamte weitere Folge. Wer den Zustand kennt oder erraten kann, kennt alle künftigen Codes.

?Was soll ich denn stattdessen benutzen?

In Python das Modul `secrets`, in anderen Sprachen den ausdrücklich für Sicherheit gedachten Generator. Die Dokumentation sagt beides in zwei Sätzen: Das Modul diene der Erzeugung „cryptographically strong random numbers suitable for managing data such as passwords, account authentication, security tokens, and related secrets“, und es solle „be used in preference to the default pseudo-random number generator in the random module, which is designed for modelling and simulation, not security or cryptography“. Der Unterschied ist kein Feinschliff: Das eine Modul ist für Würfelspiele und Simulationen gebaut, das andere für Geheimnisse.

?Reicht es nicht, mit der aktuellen Uhrzeit zu starten? Die kennt ja niemand genau.

Doch, jeder. Ein Zeitstempel in Sekunden hat pro Tag 86 400 mögliche Werte, pro Jahr 31 536 000 – das probiert ein durchschnittlicher Rechner in Sekunden durch. Selbst vorgeführt: Wer weiß, dass ein Code am Vormittag des 6. August 2026 erzeugt wurde, probiert die Sekundenwerte dieses Zeitraums durch, und für jeden einzelnen liefert derselbe Generator denselben Code wie der Server. Genau diesen Angriff zeigt der zweite Schritt unten. Der Zeitpunkt ist kein Geheimnis, sondern steht meist sogar im Datensatz daneben – als Anlagedatum.

?Woher soll echter Zufall überhaupt kommen?

Vom Betriebssystem, und das ist der eigentliche Punkt hinter `secrets`: Das Modul biete „access to the most secure source of randomness that your operating system provides“. Unter Linux ist das der Systemaufruf getrandom, dessen Handbuchseite den Zweck benennt: Er füllt einen Puffer mit Zufallsbytes, und diese Bytes „can be used to seed user-space random number generators or for cryptographic purposes“. Das ist die Quelle, die auch Schlüssel für Verschlüsselung speist. Man muss sie nicht selbst ansprechen – man muss nur die Funktion aufrufen, die es tut.

?Wir verlosen Preise per Mischen einer Liste. Ist das denn sicher zufällig?

Nicht zwangsläufig, und zwar aus einem Grund, den die Python-Dokumentation beim Mischen ausdrücklich anmerkt: Schon bei kurzen Folgen könne die Zahl der möglichen Anordnungen die Periode der meisten Zufallszahlengeneratoren übersteigen, weshalb „most permutations of a long sequence can never be generated“. Als Grenze wird genannt: „a sequence of length 2080 is the largest that can fit within the period of the Mersenne Twister random number generator“. Selbst nachgerechnet stimmt das auf den Eintrag genau – die Fakultät von 2080 liegt noch unter der Periode 2¹⁹⁹³⁷−1, die von 2081 darüber. Für eine Verlosung unter mehr als 2080 Teilnehmern sind also von vornherein die meisten Ergebnisse ausgeschlossen. Ob das rechtlich zulässig ist, entscheidet nicht die Technik – aber wer es nicht weiß, hat es auch niemandem gesagt.

Belege

Diese Seite ist gegen die folgenden Primärquellen geprüft – Hersteller, Standard oder Gesetzestext. Wer es nicht glaubt, muss es nicht: Da steht, wo es nachzulesen ist.

Zuletzt geprüft am . Dokumentation altert – ein Datum daneben ist der einzige ehrliche Umgang damit.

Auch zu finden unter

Verwandte Fehler

SWLästig429

KI-Dienst meldet 429: zu viele Anfragen

Zu viele Anfragen, Kapazität erschöpft, 429: Der KI-Dienst ist nicht ausgefallen, sondern gedeckelt. Wiederholen hilft nur bei einer der Ursachen.

SWSystemzerstörend

Programm hängt bei einem regulären Ausdruck

Ein harmloser regulärer Ausdruck lastet einen Kern zu 100 % aus und hängt? Bei bestimmten Mustern explodiert der Aufwand — katastrophales Backtracking.