Heim > Backend-Entwicklung > PHP-Tutorial > Vertiefendes Verständnis der Sicherheit von mt_rand()-Zufallszahlen in PHP

Vertiefendes Verständnis der Sicherheit von mt_rand()-Zufallszahlen in PHP

不言
Freigeben: 2023-03-29 14:58:02
Original
2392 Leute haben es durchsucht

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, auf die Sie sich beziehen können

Vorwort

Ich habe vor einiger Zeit viele Sicherheitslücken im Zusammenhang mit mt_rand() ausgegraben, und das waren sie auch Grundsätzlich Fehler. Verstehen Sie die Verwendung von Zufallszahlen. 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.
Nach dem Login kopieren

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ührt.

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 Auf dem Startwert und der aktuellen Anzahl wird i aufgerufen, um 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) );
}
Nach dem Login kopieren

Das sieht man jedes Mal, wenn mt_rand () aufgerufen wird, prüft es zunächst, ob es gesät wurde. Wenn es geseed wurde, generieren Sie direkt Zufallszahlen, andernfalls rufen Sie php_mt_srand zum Seeding auf. 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(&#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;
 }
}
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 Wir haben gerade über die automatische Aussaat gesprochen:

<?php
//pid1.php
if(isset($_GET[&#39;rand&#39;])){
 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(&#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;
 }
}
Nach dem Login kopieren

Erhalten Sie über pid Judgment, wenn ein neuer Prozess beginnt, 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

Take the erste Zufallszahl 1513334371 Explodierende 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
Nach dem Login kopieren

3 mögliche Samen explodiert, die Anzahl ist sehr klein. Testen Sie einen nach dem anderen manuell:

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

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 beliebig oft 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 teilen sie, solange die Anfrage durch denselben Prozess verarbeitet wird, denselben ursprünglich automatisch gesäten Seed.

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 von mt_rand() tatsächlich 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 dem Ausgabeergebnis 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, und der Beamte gibt auch klar an, dass die generierten Zufallszahlen nicht für Sicherheitsverschlüsselungszwecke verwendet werden sollten (obwohl dies im Handbuch der chinesischen Version nicht steht). Das Problem besteht darin, dass der Entwickler nicht erkennt, 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.


Das obige ist der detaillierte Inhalt vonVertiefendes Verständnis der Sicherheit von 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