
Der Achillesferse der Kryptographie: wie PRNG deine Sicherheit ruinieren können
Celia CatalánAktie
A02:2021 - Kryptografische Fehler – Pseudozufallszahlengeneratoren (PRNG)
Einführung
Die Generierung von Zufallszahlen in der Programmierung ist entscheidend für viele Anwendungen, insbesondere in der Kryptographie. Eine Zufallszahl ist eine Zahl, die nicht vorhergesagt oder antizipiert werden kann, was die Sicherheit bei der Generierung von Schlüsseln, Tokens oder anderen sensiblen Werten gewährleistet, die geheim bleiben müssen.
Es gibt zwei Haupttypen von Zufallszahlengeneratoren (RNG, kurz für Random Number Generator):
1. Pseudozufallszahlengeneratoren (PRNG): Sie verwenden deterministische Algorithmen, die zwar Zahlenfolgen erzeugen, die zufällig erscheinen, aber vorhersehbar sind, wenn der Anfangszustand oder die Seed bekannt ist. Dies ist in kryptografischen Kontexten gefährlich, da ein Angreifer die Folge replizieren und sensible Werte vorhersagen könnte.
2. Wahrhaft zufällige Zufallszahlengeneratoren (TRNG): Basierend auf unvorhersehbaren physikalischen Phänomenen wie elektromagnetischer Strahlung oder thermischem Rauschen sind diese Generatoren viel sicherer, da sie nicht vorhersagbar sind.
Die Verwundbarkeit entsteht, wenn unsichere oder schlecht implementierte PRNGs in kryptografischen Systemen verwendet werden. Wenn ein Angreifer die generierten Zahlen vorhersagen kann, kann er die Sicherheit der Systeme brechen, kryptografische Schlüssel ableiten, sichere Kommunikationen abfangen oder auf geschützte Ressourcen zugreifen.
Diese Beziehung zwischen Zufälligkeit und Sicherheit ist einer der Gründe, warum OWASP die Fehler in der Kryptographie, wie die Erzeugung unsicherer Zufallszahlen, zu den wichtigsten Sicherheitsbedenken zählt (A02:2021 Kryptografische Fehler).
Technische Einführung
Ein Algorithmus zur Erzeugung von Pseudozufallszahlen (PRNG) ist ein deterministisches System, das einen Anfangswert, bekannt als Seed, durch eine vordefinierte mathematische Funktion transformiert, um eine Sequenz von Zahlen zu erzeugen, die auf den ersten Blick zufällig erscheinen. Diese Zufälligkeit ist jedoch nur oberflächlich, da die Sequenz vollständig durch den ursprünglichen Seed bestimmt ist. Das bedeutet, dass zwei Instanzen desselben Algorithmus, wenn sie mit demselben Seed initialisiert werden, eine identische Sequenz von Zahlen erzeugen. Obwohl diese Eigenschaft in bestimmten Kontexten, wie in reproduzierbaren Simulationen, nützlich sein kann, ist sie in der Kryptographie äußerst problematisch, da die Vorhersehbarkeit die grundlegenden Sicherheitsprinzipien untergräbt.
Aus der Sicht der kryptografischen Sicherheit hängt man stark von der Qualität und Unvorhersehbarkeit der Zufallszahlen ab, die in Prozessen wie der Schlüsselerzeugung, Initialisierungsvektoren und Nonces verwendet werden. Traditionelle PRNGs, wie die linearen kongruenziellen Generatoren (LCG), sind aufgrund ihrer einfachen mathematischen Struktur und der geringen Entropie, die in den Saaten verwendet wird, anfällig für Angriffe. Diese Generatoren sind oft auf vorhersehbare Werte angewiesen, wie Zeitstempel, die ein Angreifer ableiten kann.
Im Gegensatz zu traditionellen PRNGs sind kryptographisch sichere Pseudozufallszahlengeneratoren (CSPRNG) so konzipiert, dass sie Angriffen standhalten, selbst wenn ihr interner Zustand teilweise offengelegt wird oder wenn mehrere Ausgaben der Sequenz abgefangen werden. CSPRNG verwenden robustere Entropiequellen, dies kann ein hardwarebasierter Pseudozufallszahlengenerator oder sogar Prozesse aus unvorhersehbaren Systemen sein, und wenden fortschrittliche kryptographische Algorithmen an, wie z. B. solche, die auf Blockverschlüsselungen oder Hash-Funktionen basieren, um die Unvorhersehbarkeit der Sequenz zu gewährleisten.
Hauptanfälligkeiten
Vorhersehbarkeit bei der Erzeugung kryptografischer Schlüssel: Die Vorhersehbarkeit unsicherer PRNGs gefährdet die Erzeugung kryptografischer Schlüssel. Wenn ein Angreifer es schafft, den verwendeten Seed S_0 zu erschließen, sei es durch eine Analyse der Ausgaben des Generators oder durch das Erraten eines Seeds basierend auf einem vorhersehbaren Wert wie einem Zeitstempel, kann er den zugehörigen privaten Schlüssel rekonstruieren und Nachrichten entschlüsseln oder betrügerische Signaturen erstellen. Dieses Problem hat historisch gesehen Algorithmen wie RSA und ECC betroffen, bei denen die Qualität der Schlüssel direkt von der Qualität der Zufallszahlen abhängt.
Wiederverwendete oder vorhersagbare Nonces: In kryptografischen Systemen wie AES-GCM oder ChaCha20-Poly1305 gefährdet die Wiederverwendung derselben Nonce (eindeutige Nummer pro Nachricht) die Vertraulichkeit und Integrität der Daten. Wenn ein PRNG vorhersagbare Nonces N generiert oder Werte aufgrund von Kollisionen in einem kleinen Raum wiederverwendet, kann ein Angreifer Angriffe wie die Wiederherstellung von Klartext durch Differenzanalyse durchführen. Ein bemerkenswerter Fall ist die Wiederverwendung von Nonces in der Implementierung von WPA2, die Angriffe wie KRACK ermöglichte.
Verwundbare JWT-Sitzungen: JSON Web Tokens (JWT) hängen von einzigartigen und zufälligen Werten ab, um zu verhindern, dass sie repliziert oder erraten werden. Wenn ein unsicherer PRNG Sitzungs-IDs oder vorhersehbare Signaturwerte generiert, könnte ein Angreifer gültige Tokens fälschen, um auf geschützte Systeme zuzugreifen. Zum Beispiel könnte eine HMAC-Signatur, die auf aus vorhersehbaren Pseudozufallszahlen abgeleiteten Schlüsseln basiert, einen Identitätsdiebstahl-Angriff mit reduziertem Rechenaufwand ermöglichen.
Probleme bei Handshake-Protokollen: Protokolle wie TLS und SSH hängen von Zufallswerten ab, um sicherzustellen, dass die Sitzungen einzigartig sind und nicht von einem Angreifer reproduziert werden können. Wenn die im Handshake verwendeten Pseudozufallszahlen vorhersagbar sind, könnte ein Angreifer "Replay"-Angriffe durchführen oder die Neuverhandlung schwacher Schlüssel erzwingen, was die Vertraulichkeit des Kanals gefährdet. Dies wurde in älteren Versionen von SSL/TLS festgestellt, die mangelhafte PRNGs verwendeten.
Sichere vs. unsichere Generierung vergleichen
Die Generation kann in drei Hauptschritte unterteilt werden:
1. Beschaffung des Ausgangsamens:
In der unsicheren Generierung stammt der Seed oft aus leicht vorhersagbaren Quellen, wie einem Zeitstempel, der Prozess-ID (PID) oder sogar einem konstanten Wert. Zum Beispiel kann ein linearer kongruenter Generator die aktuelle Zeit in Sekunden verwenden, was den Suchraum für einen Angreifer drastisch reduziert. Auf der anderen Seite stammt der Seed in der sicheren Generierung aus einer robusten Entropiequelle, wie internen Zuständen, die vom Betriebssystem gesammelt werden. Dies stellt sicher, dass der Seed nicht vorhersagbar oder reproduzierbar ist.
2. Berechnung des inneren Zustands:
Ein unsicherer PRNG transformiert den ursprünglichen Seed durch einfache mathematische Operationen wie Multiplikationen oder modulare Additionen, die, obwohl sie scheinbar zufällige Zahlen erzeugen, bei ausreichender Analyse erkennbare Muster aufweisen. Im Gegensatz dazu wendet ein sicherer Generator kryptografische Algorithmen wie Hash-Funktionen oder Blockverschlüsselungen an, die eine hohe Widerstandsfähigkeit gegen die Rückführung des internen Zustands besitzen.
3. Produktion der Zahlenfolge:
Unsichere PRNGs erzeugen jede Zahl in der Sequenz durch schnelle, aber deterministische Formeln, was es ermöglicht, zukünftige Ausgaben vorherzusagen, wenn der aktuelle Zustand bekannt ist. Zum Beispiel könnte ein Angreifer, der einige aufeinanderfolgende Ausgaben beobachtet, den Algorithmus ableiten und die nächsten vorhersagen (in extrem einfachen Fällen). Im Vergleich dazu erzeugen sichere Generatoren Zahlen unter Verwendung von Transformationen, die sowohl vom internen Zustand als auch von der während des Betriebs gesammelten zusätzlichen Entropie abhängen, wodurch jede Ausgabe praktisch unvorhersehbar wird.
Gehen wir zum Code ...
#include (stdio .h="")
#include (stdlib .h="")
#include (time .h="")
int generate_random_number() {
static int initialisiert = 0;
if (!initialisiert) {
srand((unsigned int)time(NULL)); // (-- Verwendung des Zeitstempels als Saat
initialisiert = 1;
}
return rand() % 100;
}
int main() {
printf(Generiere Pseudozufallszahlen:
);
für (int i = 0; i (5,i++) {
printf(%d\n, generate_random_number());
}
Rückgabe 0;
}
- Verwendung von syscall wie getrandom(), das eine kryptografisch sichere Entropiequelle bereitstellt, die von Linux angeboten wird (erfordert glibc Version 2.25 oder höher).
- Wenn das System getrandom() nicht unterstützt, kann /dev/urandom verwendet werden, um Zufallszahlen ähnlicher Qualität zu erhalten. In Windows-Systemen wäre die entsprechende Funktion BCryptGenRandom.
- Alternativ kann in CPUs mit RDRAND (Intel/AMD) _rdrand32_step verwendet werden, um Zahlen direkt aus der Hardware zu erhalten.
#include (fcntl.h)
#include (errno.h)
#include (string.h)
#include (linux/random.h)
#include (sys/syscall.h)
int generate_secure_random_number() {
uint32_t zufallszahl;
// Usar syscall getrandom para obtener un número aleatorio seguro
if (syscall(SYS_getrandom, random_number, sizeof(random_number), 0) == -1) {
perror("Fehler beim Generieren einer sicheren Zufallszahl");
Ausfahrt (EXIT_FAILURE);
}
return random_number % 100;
}
int main() {
printf("Sichere Zufallszahlen generieren:
");
für (int i = 0; i < 5; i++) {
printf(%d\n, generate_secure_random_number());
}
Rückgabe 0;
}