Heim  >  Artikel  >  Backend-Entwicklung  >  Detailliertes Verständnis der Zufallszahlensicherheit mt_rand() in PHP

Detailliertes Verständnis der Zufallszahlensicherheit mt_rand() in PHP

黄舟
黄舟Original
2017-10-13 09:42:404178Durchsuche

mt_rand() verwendet den Mersennetwister-Algorithmus, um zufällige Ganzzahlen zurückzugeben. Der folgende Artikel stellt Ihnen jedoch hauptsächlich die relevanten Informationen zur Sicherheit von mt_rand()-Zufallszahlen in PHP vor. Sie brauchen Freunde, die darauf verweisen können. Lassen Sie uns mit dem untenstehenden Herausgeber lernen.

Vorwort

Ich habe vor einiger Zeit viele Sicherheitslücken im Zusammenhang mit mt_rand() entdeckt. Sie haben die Verwendung von Zufallszahlen grundsätzlich missverstanden. verursacht. Hier möchte ich einen weiteren Fallstrick des offiziellen PHP-Website-Handbuchs erwähnen. Schauen Sie sich die Einführung zu mt_rand() an: Chinesische Version ^cn Englische Version ^en Sie können sehen, dass die englische Version eine zusätzliche gelbe Warnung enthält >


This function does not generate cryptographically secure values, and should not be used for cryptographic purposes. If you need a cryptographically secure value, consider using random_int(), random_bytes(), or openssl_random_pseudo_bytes() instead.
Viele inländische Entwickler haben wahrscheinlich die chinesische Version der Einführung gelesen und mt_rand() im Programm verwendet, um Sicherheitstoken, Kernverschlüsselungs- und Entschlüsselungsschlüssel usw. zu generieren schwerwiegende Sicherheitsprobleme.

Pseudozufallszahl

mt_rand() ist keine echte Zufallszahlengenerierungsfunktion. Tatsächlich sind die meisten Programmiersprachen The Zufallszahlenfunktionen erzeugen insgesamt Pseudozufallszahlen. Der Unterschied zwischen echten Zufallszahlen und Pseudozufallszahlen wird hier nicht erläutert. Sie müssen ihn nur kurz verstehen

Pseudozufälligkeit wird durch eine bestimmbare Funktion (häufig verwendete lineare Kongruenz) durch einen Startwert erzeugt (häufig verwendete Uhr) von Pseudozufallszahlen. Das heißt, wenn man den Startwert bzw. die generierte Zufallszahl kennt, ist es möglich, Informationen über die nächste Zufallszahlenfolge zu erhalten (Vorhersagbarkeit).

Nehmen Sie einfach an, dass die Funktion, die intern in mt_rand() Zufallszahlen generiert, wie folgt lautet:

wobei „seed“ der Zufallszahlen-Seed ist und i die Häufigkeit angibt, mit der diese Zufallszahlenfunktion aufgerufen wird. Wenn wir die beiden Werte von i und rand gleichzeitig kennen, können wir den Wert von Seed leicht berechnen. Beispielsweise werden rand=21 und i=2 in die Funktion 21=seed+(2*10) eingesetzt, um Seed=1 zu erhalten. Ist es nicht sehr einfach? Nachdem wir den Startwert erhalten haben, können wir den Wert von Rand berechnen, wenn i ein beliebiger Wert ist. rand = seed+(i*10)

PHPs automatisches Seeding

Aus dem vorherigen Abschnitt wissen wir bereits, dass jedes Mal, wenn mt_rand() aufgerufen wird, es basiert Rufen Sie i mehrmals auf, um den Startwert und den aktuellen Wert zu ermitteln und eine Pseudozufallszahl zu berechnen. Und der Seed wird automatisch gesetzt:

Hinweis: Seit PHP 4.2.0 besteht keine Notwendigkeit, srand() oder mt_srand() zum Seeding des Zufallszahlengenerators zu verwenden, da dies jetzt automatisch vom System erfolgt .

Dann stellt sich die Frage, wann das System das Seeding automatisch abschließt. Wenn mt_rand() jedes Mal automatisch geseed wird, macht es keinen Sinn, das Seed zu knacken. Das Handbuch gibt hierzu keine detaillierten Informationen. Ich habe im ganzen Internet gesucht und keine verlässliche Antwort gefunden, also musste ich den Quellcode ^mtrand durchgehen:


PHPAPI void php_mt_srand(uint32_t seed)
{
 /* Seed the generator with a simple uint32 */
 php_mt_initialize(seed, BG(state));
 php_mt_reload();

 /* Seed only once */
 BG(mt_rand_is_seeded) = 1; 
}
/* }}} */

/* {{{ php_mt_rand
 */
PHPAPI uint32_t php_mt_rand(void)
{
 /* Pull a 32-bit integer from the generator state
 Every other access function simply transforms the numbers extracted here */

 register uint32_t s1;

 if (UNEXPECTED(!BG(mt_rand_is_seeded))) {
 php_mt_srand(GENERATE_SEED());
 }

 if (BG(left) == 0) {
 php_mt_reload();
 }
 --BG(left);

 s1 = *BG(next)++;
 s1 ^= (s1 >> 11);
 s1 ^= (s1 << 7) & 0x9d2c5680U;
 s1 ^= (s1 << 15) & 0xefc60000U;
 return ( s1 ^ (s1 >> 18) );
}
Das sieht man jedes Mal, wenn mt_rand( ) aufgerufen wird, wird zunächst geprüft, ob gesät wurde. Wenn es gesät wurde, generieren Sie direkt Zufallszahlen, andernfalls rufen Sie php_mt_srand auf, um zu säen. Das heißt, während jedes PHP-CGI-Prozesses wird nur der erste Aufruf von mt_rand() automatisch geseedet. Als nächstes werden Zufallszahlen basierend auf diesem ersten gesäten Samen generiert. Unter den verschiedenen Betriebsmodi von PHP, außer CGI (jede Anfrage startet einen CGI-Prozess und schließt ihn, nachdem die Anfrage abgeschlossen ist). Die Umgebungsvariablen von php.ini müssen jedes Mal neu gelesen werden, was zu einer geringen Effizienz führt, und das sollte auch nicht der Fall sein Wird jetzt häufig verwendet. Nachdem ein Prozess die Anforderung verarbeitet hat, wartet der Standby-Prozess im Grunde auf die nächste und wird erst wiederverwendet, wenn mehrere Anforderungen verarbeitet wurden (er wird auch nach einer Zeitüberschreitung wiederverwendet).

Schreiben Sie ein Skript, um es zu testen


<?php
//pid.php
echo getmypid();


<?php
//test.php
$old_pid = file_get_contents(&#39;http://localhost/pid.php&#39;);
$i=1;
while(true){
 $i++;
 $pid = file_get_contents(&#39;http://localhost/pid.php&#39;);
 if($pid!=$old_pid){
 echo $i;
 break;
 }
}
Testergebnis: (windows+phpstudy)

Apache 1000 Anfragen

Nginx 500 Anfragen

Natürlich bestätigt dieser Test nur die Anzahl der Anfragen, die Apache und Nginx in einem Prozess verarbeiten können. Lassen Sie uns die Schlussfolgerung einfach überprüfen Nun zur automatischen Aussaat:


<?php
//pid1.php
if(isset($_GET[&#39;rand&#39;])){
 echo mt_rand();
}else{
 echo getmypid();
}


<?php
//pid2.php
echo mt_rand();


<?php
//test.php
$old_pid = file_get_contents(&#39;http://localhost/pid1.php&#39;);
echo "old_pid:{$old_pid}\r\n";
while(true){
 $pid = file_get_contents(&#39;http://localhost/pid1.php&#39;);
 if($pid!=$old_pid){
 echo "new_pid:{$pid}\r\n";
 for($i=0;$i<20;$i++){
  $random = mt_rand(1,2);
  echo file_get_contents("http://localhost/pid".$random.".php?rand=1")." ";
 }

 break;
 }
}
Gemessen an der PID Wenn ein neuer Prozess beginnt, erhalten Sie zufällig die Ausgabe von mt_rand() auf einer der beiden Seiten:


old_pid:972 new_pid:7752 1513334371 2014450250 1319669412 499559587 117728762 1465174656 1671827592 1703046841 464496438 1974338231 46646067 981271768 1070717272 571887250 922467166 606646473 134605134 857256637 1971727275 2104203195
Nehmen Sie die erste Zufallszahl 1513334371, um sie zu explodieren der Samen:


smldhz@vm:~/php_mt_seed-3.2$ ./php_mt_seed 1513334371 Found 0, trying 704643072 - 738197503, speed 28562751 seeds per second seed = 735487048 Found 1, trying 1308622848 - 1342177279, speed 28824291 seeds per second seed = 1337331453 Found 2, trying 3254779904 - 3288334335, speed 28811010 seeds per second seed = 3283082581 Found 3, trying 4261412864 - 4294967295, speed 28677071 seeds per second Found 3
3 mögliche Samen aufgelöst, die Anzahl ist sehr gering und einzeln manuell getestet:


<?php
mt_srand(735487048);//手工播种
for($i=0;$i<21;$i++){
 echo mt_rand()." ";
}
Ausgabe:

Die ersten 20 Ziffern sind genau die gleichen wie die, die durch das obige Skript erhalten wurden. Der bestätigte Startwert ist 1513334371. Mit dem Startwert können wir die Zufallszahl berechnen, die durch einen beliebigen Aufruf von mt_rand() generiert wird. Zum Beispiel habe ich in diesem Skript 21 Ziffern generiert, und die letzte Ziffer ist 1515656265. Wenn Sie die Site nach der Ausführung des Skripts gerade noch nicht besucht haben, können Sie dieselbe 1515656265 sehen, indem Sie http://localhost/pid2.php öffnen.

Wir kommen also zu dem Schluss:

Das automatische Seeding von PHP erfolgt, wenn mt_rand() zum ersten Mal im PHP-CGI-Prozess aufgerufen wird. Unabhängig von der besuchten Seite wird, solange die Anfrage vom gleichen Prozess bearbeitet wird, derselbe ursprünglich automatisch gesäte Seed verwendet.

php_mt_seed

Wir wissen bereits, dass die Erzeugung von Zufallszahlen von einer bestimmten Funktion abhängt, die oben als rand = seed+(i*10)  angenommen wurde. Für eine so einfache Funktion können wir natürlich direkt (mündlich) eine Lösung (Gruppe) berechnen, aber die tatsächlich von mt_rand() verwendete Funktion ist recht komplex und kann nicht invertiert werden. Eine effektive Cracking-Methode besteht darin, alle Seeds umfassend aufzuzählen, eine Zufallszahlenfolge basierend auf dem Seed zu generieren und diese dann mit einer bekannten Zufallszahlenfolge zu vergleichen, um zu überprüfen, ob der Seed korrekt ist. php_mt_seed^phpmtseed ist ein solches Tool. Es ist sehr schnell und die Ausführung des 2^32-Bit-Seeds dauert nur wenige Minuten. Es kann mögliche Seeds basierend auf der Ausgabe eines einzelnen mt_rand() direkt auflösen (Beispiel oben). Natürlich kann es auch Seeds auflösen, die die MIN-MAX-Ausgabe begrenzen, wie mt_rand(1,100) (nützlich in den folgenden Beispielen).

Sicherheitsprobleme

Nachdem ich so viel gesagt habe: Warum sind Zufallszahlen unsicher? Tatsächlich ist an der Funktion selbst nichts auszusetzen. Der Beamte stellt auch klar, dass die generierten Zufallszahlen nicht für Sicherheitsverschlüsselungszwecke verwendet werden sollten (obwohl dies im Handbuch der chinesischen Version nicht steht). Das Problem ist, dass die Entwickler nicht erkennen, dass es sich hierbei nicht um eine echte Zufallszahl handelt. Wir wissen bereits, dass Samen aus einer bekannten Folge von Zufallszahlen explodiert werden können. Mit anderen Worten: Solange es auf einer Seite eine Ausgabe-Zufallszahl oder deren Ableitungswert (umkehrbaren Zufallswert) gibt, ist die Zufallszahl auf einer anderen Seite keine „Zufallszahl“ mehr. Zu den gängigen Beispielen für die Ausgabe von Zufallszahlen gehören Verifizierungscodes, zufällige Dateinamen usw. Zur Sicherheitsüberprüfung werden gängige Zufallszahlen verwendet, beispielsweise zum Abrufen von Kennwortüberprüfungswerten wie Verschlüsselungsschlüsseln usw. Ein ideales Angriffsszenario:

Wenn Sie mitten in der Nacht darauf warten, dass Apache (Nginx) alle PHP-Prozesse zurücknimmt (um sicherzustellen, dass der nächste Besuch erneut gestartet wird), besuchen Sie einmal die Bestätigungscodeseite. Kehren Sie die Zufallszahl basierend auf den Zeichen des Bestätigungscodes um und explodieren Sie dann Zufallszahlen-Seeds basierend auf Zufallszahlen. Besuchen Sie dann die Seite zum Passwortabruf. Der generierte Link zum Passwortabruf basiert auf Zufallszahlen. Wir können diesen Link einfach berechnen und das Administratorkennwort abrufen...XXOO

Instanz

PHPCMS MT_RAND SEED Crack führt zu Authkey Leck. Yu Niu schreibt besser als ich, lesen Sie einfach sein

Discuz x3.2 Authkey Leak ist tatsächlich ähnlich. Der offizielle Patch wurde veröffentlicht und Interessierte können ihn selbst analysieren.

Zusammenfassung

Das obige ist der detaillierte Inhalt vonDetailliertes Verständnis der Zufallszahlensicherheit mt_rand() in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn