Ruby: Identische Seeds für den Pseudo-Zufallszahlen-Generator

Schon vor einigen Monaten wurde gemeldet, dass der Pseudo Random Number Generator (PRNG) von OpenSSL die Prozess-ID (PID) verwendet, um den Zufallszahlen-Seed zu setzen. Da PIDs im Laufe der Zeit von einem System wiederverwendet werden, kann es daher vorkommen, dass unsichere Zufallszahlen generiert werden, die einfach identisch sind. Unter Ruby hat sich dieses Problem in dem auf OpenSSL basierenden Paket “openssl” im Zusammenhang mit “fork” bemerkbar gemacht. Ein Angreifer kann dies ausnutzen, um Zufallszahlen vorherzusagen.

Folgendes Ruby-Beispiel wurde hierzu veröffentlicht:

require "openssl"
require "securerandom"
SecureRandom.random_bytes(4)
pid = fork do p [ $$, SecureRandom.random_bytes(4) ]
end
Process.waitpid2(pid)
loop do xpid = fork do p [ $$, SecureRandom.random_bytes(4) ] if $$ == pid end Process.waitpid2(xpid) break if xpid == pid
end

“SecureRandom.random_bytes” ist auf Systemen mit instaliertem OpenSSL lediglich ein Wrapper für “OpenSSL::Random.random_bytes”. Das Skript durchläuft die Schleife so lange, bis “fork” einen Prozess mit identischer PID erzeugt. Dann bricht das Skript ab und gibt die entsprechenden Zufallszahlen aus. Aufgrund der identischen PID sind diese gleich, was das Problem demonstriert.

Obwohl es sich bei diesem Problem prinzipiell um ein OpenSSL-Feature handelt, hat die Ruby-Community mehrere Patches in den Code eingebaut, um dieses Fork-PRNG-Problem zu lösen. So wurde beispielsweise in die Funktion “rb_thread_atfork()” (“eval.c”) ein Aufruf von “rb_reset_random_seed()” eingefügt, um den Seed neu zu setzen.

Auf der OpenSSL-Entwickler-Mailingliste wurde dieses OpenSSL-Feature ebenfalls kurz diskutiert. Dabei stellten die Teilnehmer schon am Anfang der Diskussion klar, dass es keine gute Idee sein, “fork()” zusammen mit PRNGs zu verwenden. Auf der Mailingliste findet sich auch ein C-Programm von Eric Wong, welches mit “fork()” und “RAND_bytes()” das Problem demonstriert:

#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <openssl/rand.h>
static void dump_random(void)
{ int i; unsigned char buf[4]; RAND_bytes(buf, sizeof(buf)); printf("pid=%d ", getpid()); for (i = 0; i < sizeof(buf); i++) printf("\\x%02x", buf[i]); puts("");
}
int main(void)
{ pid_t pid, xpid; /* PRNG needs to be initialized in original process to reproduce */ RAND_bytes((unsigned char *)&pid, sizeof(pid)); pid = fork(); if (pid == 0) { dump_random(); return 0; } else if (pid > 0) { wait(NULL); do { xpid = fork(); if (xpid == 0) { if (getpid() == pid) dump_random(); return 0; } else if (xpid > 0) { wait(NULL); } else { perror("fork"); } } while (pid != xpid); } else { perror("fork"); return 1; } return 0;
}

Wie das Ruby-Skript läuft das Programm so lange, bis Prozesse mit identischer PID erzeugt wurden. Dann zeigt es zwei identische Zufallszahlenreihen an. Im Prinzip wäre es möglich, die OpenSSL-Bibliothek derart umzuschreiben, dass so etwas nicht passiert. Das würde jedoch in zahlreichen derzeit vorhandenen OpenSSL-Anwendungen zu Problemen führen, wie auf der Mailingliste diskutiert wird.

Betroffen sind Ruby-Versionen vor 1.8.7-p352.

Nach oben