Die Fähigkeit, den nächsten Wert zu erraten, rand
hängt davon ab, zu bestimmen, womit srand
aufgerufen wurde. Insbesondere das Säen srand
mit einer vorbestimmten Anzahl führt zu einer vorhersehbaren Ausgabe ! Über die interaktive PHP-Eingabeaufforderung:
[charles@charles-workstation ~]$ php -a
Interactive shell
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php >
Dies ist nicht nur ein Zufall. Die meisten PHP-Versionen * auf den meisten Plattformen ** erzeugen srand
bei 1024 die Sequenz 97, 97, 39, 77, 93 .
Um es klar auszudrücken, dies ist kein Problem mit PHP, dies ist ein Problem mit der Implementierung von sich rand
selbst. Dasselbe Problem tritt in anderen Sprachen auf, die dieselbe (oder eine ähnliche) Implementierung verwenden, einschließlich Perl.
Der Trick ist, dass jede vernünftige Version von PHP srand
einen "unbekannten" Wert hat. Oh, aber es ist nicht wirklich unbekannt. Von ext/standard/php_rand.h
:
#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
Also ist es ein bisschen Mathe mit time()
, der PID und dem Ergebnis von php_combined_lcg
, das in definiert ist ext/standard/lcg.c
. Ich werde nicht hierher kommen, da meine Augen glasig wurden und ich beschloss, die Jagd einzustellen.
Ein bisschen googeln zeigt, dass andere Bereiche von PHP nicht die besten Eigenschaften für die Zufallsgenerierung haben , und fordert dazu auf php_combined_lcg
, hier hervorzuheben , insbesondere diese Art der Analyse:
Diese Funktion ( gettimeofday
) gibt uns nicht nur einen präzisen Server-Zeitstempel auf einem Silbertablett zurück, sondern erhöht auch die LCG-Ausgabe, wenn wir "mehr Entropie" (von PHPs uniqid
) anfordern .
Ja dasuniqid
. Es scheint, dass der Wert von das php_combined_lcg
ist, was wir sehen, wenn wir die resultierenden hexadezimalen Ziffern betrachten, nachdem wir uniqid
das zweite Argument auf einen wahren Wert gesetzt haben.
Wo waren wir jetzt?
Oh ja. srand
.
Wenn der Code, aus dem Sie zufällige Werte vorhersagen möchten, nicht aufgerufen wird srand
, müssen Sie den von bereitgestellten Wert ermitteln php_combined_lcg
, den Sie (indirekt?) Durch einen Aufruf von abrufen können uniqid
. Mit diesem Wert in der Hand ist es möglich , den Rest des Werts - time()
, die PID und etwas Mathematik - brachial zu erzwingen . Bei dem verknüpften Sicherheitsproblem geht es um das Unterbrechen von Sitzungen, aber die gleiche Technik würde hier funktionieren. Wieder aus dem Artikel:
Hier ist eine Zusammenfassung der oben beschriebenen Angriffsschritte:
- Warten Sie, bis der Server neu gestartet wurde
- Holen Sie sich einen eindeutigen Wert
- Brute Force das RNG-Saatgut aus diesem
- Fragen Sie den Online-Status ab, um zu warten, bis das Ziel angezeigt wird
- Verschachteln Sie Statusabfragen mit eindeutigen Abfragen, um die aktuelle Serverzeit und den aktuellen RNG-Wert zu verfolgen
- Brute Force-Sitzungs-ID für den Server unter Verwendung der Zeit und des RNG-Wertintervalls, die bei der Abfrage festgelegt wurden
Ersetzen Sie einfach den letzten Schritt nach Bedarf.
(Dieses Sicherheitsproblem wurde in einer früheren PHP-Version (5.3.2) gemeldet als derzeit (5.3.6). Daher ist es möglich, dass sich das Verhalten von uniqid
und / oder php_combined_lcg
geändert hat, sodass diese spezielle Technik möglicherweise nicht mehr funktioniert. YMMV.)
Auf der anderen Seite, wenn der Code , den Sie Produkt sind versuchen Anrufe srand
manuell , dann , wenn sie nicht verwenden etwas oft besser als das Ergebnis php_combined_lcg
, sind Sie wahrscheinlich eine viel haben , gehen leichte Zeit zu raten , den Wert und Säen Sie Ihre lokalen Generator mit der richtigen Nummer. Die meisten Leute, die manuell anrufen srand
würden , würden auch nicht erkennen, wie schrecklich eine Idee ist, und werden daher wahrscheinlich keine besseren Werte verwenden.
Es ist erwähnenswert, dass mt_rand
das gleiche Problem auch betroffen ist. Das Säen mt_srand
mit einem bekannten Wert führt auch zu vorhersehbaren Ergebnissen. Es openssl_random_pseudo_bytes
ist wahrscheinlich sicherer, Ihre Entropie zu begründen .
tl; dr: Um die besten Ergebnisse zu erzielen , sollten Sie den PHP-Zufallszahlengenerator nicht verwenden. Um Himmels willen, sollten Sie ihn nicht uniqid
den Benutzern aussetzen . Wenn Sie eine oder beide dieser Methoden anwenden, sind Ihre Zufallszahlen möglicherweise besser zu erraten.
Update für PHP 7:
PHP 7.0 führt random_bytes
und random_int
als Kernfunktionen ein. Sie verwenden die CSPRNG-Implementierung des zugrunde liegenden Systems, wodurch sie frei von den Problemen sind, die ein Start-Zufallszahlengenerator hat. Sie sind praktisch ähnlich openssl_random_pseudo_bytes
, nur ohne dass eine Erweiterung installiert werden muss. Für PHP5 ist eine Polyfill verfügbar .
*: Der Suhosin-Sicherheitspatch ändert das Verhalten von rand
und mt_rand
so, dass sie bei jedem Anruf neu generiert werden . Suhosin wird von Dritten bereitgestellt. Einige Linux-Distributionen enthalten es standardmäßig in ihren offiziellen PHP-Paketen, während andere es als Option festlegen und andere es vollständig ignorieren.
**: Abhängig von der Plattform und den verwendeten zugrunde liegenden Bibliotheksaufrufen werden andere Sequenzen generiert als hier dokumentiert. Die Ergebnisse sollten jedoch weiterhin wiederholbar sein, es sei denn, der Suhosin-Patch wird verwendet.