Detaillierte Erklärung sicherer Beispiele für mt_rand()-Zufallszahlen in PHP

小云云
Freigeben: 2023-03-19 10:54:02
Original
2167 Leute haben es durchsucht

Viele Sicherheitslücken im Zusammenhang mit mt_rand() wurden vor einiger Zeit entdeckt und wurden im Wesentlichen durch ein Missverständnis der Verwendung von Zufallszahlen verursacht. Hier möchte ich einen weiteren Fallstrick des offiziellen PHP-Website-Handbuchs erwähnen. Schauen Sie sich die Einführung von mt_rand() an: Chinesische Version ^cn Englische Version ^en Sie können sehen, dass die englische Version eine zusätzliche gelbe Warnung enthält. mt_rand() verwendet den Mersennetwister-Algorithmus, um zufällige Ganzzahlen zurückzugeben, aber der folgende Artikel führt Sie hauptsächlich in die relevanten Informationen zur Sicherheit von mt_rand()-Zufallszahlen in PHP ein Wenn Sie es benötigen, können Sie darauf verweisen. Lassen Sie uns mit dem unten stehenden Herausgeber lernen.

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, was zu ernsthaften Sicherheitsproblemen führte.

Pseudozufallszahl

mt_rand() ist keine echte Zufallszahlengenerierungsfunktion. Tatsächlich generieren die Zufallszahlenfunktionen in den meisten Programmiersprachen Pseudozufallszahlen. Nummer. 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: rand = samen+(i*10), wobei samen der Zufallszahlen-Seed ist und i die Häufigkeit ist, mit der diese Zufallszahlenfunktion auftritt angerufen. 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.

PHPs automatisches Seeding

Aus dem vorherigen Abschnitt wissen wir bereits, dass bei jedem Aufruf von mt_rand() eine Pseudozufallszahl basierend auf dem Seed und der aktuellen Anzahl berechnet wird ruft mich an. 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, konnte aber keine verlässliche Antwort finden. Ich konnte mir nur den Quellcode ansehen ^mtrand:

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) );
}
Nach dem Login kopieren

Sie können sehen, dass dies bei jedem Aufruf von mt_rand() der Fall ist Überprüfen Sie, ob es 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();
Nach dem Login kopieren
<?php
//test.php
$old_pid = file_get_contents('http://localhost/pid.php');
$i=1;
while(true){
 $i++;
 $pid = file_get_contents('http://localhost/pid.php');
 if($pid!=$old_pid){
 echo $i;
 break;
 }
}
Nach dem Login kopieren

Testergebnisse: (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. Überprüfen wir jetzt die Schlussfolgerung zum automatischen Seeding:

<?php
//pid1.php
if(isset($_GET['rand'])){
 echo mt_rand();
}else{
 echo getmypid();
}
Nach dem Login kopieren
<?php
//pid2.php
echo mt_rand();
Nach dem Login kopieren
<?php
//test.php
$old_pid = file_get_contents('http://localhost/pid1.php');
echo "old_pid:{$old_pid}\r\n";
while(true){
 $pid = file_get_contents('http://localhost/pid1.php');
 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;
 }
}
Nach dem Login kopieren

Wenn ein neuer Prozess beginnt, erhalten Sie nach pid-Beurteilung 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
Nach dem Login kopieren

Nehmen Sie die erste Zufallszahl 1513334371, um den Startwert zu explodieren:

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
Nach dem Login kopieren

3 mögliche Samen explodiert, die Anzahl ist sehr gering. Manuell einzeln getestet:

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

Ausgabe:

Die ersten 20 sind genau die gleichen wie die erhaltenen durch das obige Skript ist der bestätigte Startwert 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 = samen+(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 wie mt_rand(1,100) begrenzen (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 leicht berechnen und das Administratorkennwort abrufen ... XXOO

Beispiel

PHPCMS MT_RAND SEED Crack verursacht Authkey-Leckage Yu Niu schreibt besser als ich, schauen Sie sich nur an, was er sagte

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

Verwandte Empfehlungen:

PHPs wahre und falsche Zufallszahlen

PHPs rand()-Funktion zur Zufallszahlengenerierung

Beispiel für die JavaScript-Implementierung eines Zufallszahlen-Deduplizierungsgenerators

Das obige ist der detaillierte Inhalt vonDetaillierte Erklärung sicherer Beispiele für mt_rand()-Zufallszahlen in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage