2009-08-21 12 views
15

ich einen Ersatz für rand() Funktion PHP benötigen, die eine kryptographisch starke Zufallszahlengenerator verwendet.rand() mit openssl_random_pseudo_bytes() ersetzen

Die openssl_random_pseudo_bytes() Funktion bekommt man den starken Generator Zufallszahl zugreifen, aber es gibt seine Daten als Byte-String. Stattdessen brauche ich eine ganze Zahl zwischen 0 und X.

Ich stelle mir vor, der Schlüssel ist, um die Ausgabe von openssl_random_pseudo_bytes() in eine ganze Zahl zu bekommen, dann können Sie jede Mathe auf sie tun, die Sie benötigen. Ich kann mir ein paar "Brute-Force-Wege" vorstellen, wie man aus einer Byte-Zeichenkette in eine Ganzzahl konvertiert, aber ich habe auf etwas ... elegantes gehofft.

+0

mt_rand() geben viel bessere Qualität als Zufallszahlen rand(), aber es ist nicht verschlüsselt entweder stark. – David

+0

Vorsicht, diese Antwort ist falsch. genauer gesagt, der Teil "% $ range" ist falsch. Nehmen Sie das folgende Szenario: Sie möchten Zahlen zwischen 1 (einschließlich) und 4 (ausschließlich) generieren (da Sie max-min ausführen). $ range = 3. Jetzt teilt sich 3 nicht gleichmäßig in 2^8, was bedeutet, dass 1 häufiger als 3 sein wird. Sei sehr vorsichtig, du wolltest einen sicheren Zufallszahlengenerator machen, aber hast ihn versehentlich unsicher gemacht, deshalb Es wird empfohlen, vorhandene Implementierungen zu verwenden. – chacham15

+0

@ chacham15: Wenn Sie richtig verstehen, erhalten Sie in einem 8-Bit-Bereich 85 Sätze [1,2,3] plus eine einzelne [1,] übrig, was eine "1" um 1,17% wahrscheinlicher macht als " 2 "oder" 3 ", oder? Wie würdest du dieses Problem lösen? Natürlich ist die Verwendung einer * vorhandenen Implementierung * keine Option, oder ich hätte die Frage nicht gestellt. – tylerl

Antwort

11

bereitgestellt Vorschläge verwenden, ich habe einen Tropfen erstellt -in Ersatz für rand() mit OpenSSL. Ich werde es hier für die Nachwelt aufnehmen.

Die Option $ pedantic gibt vorbelastete Ergebnisse, indem von vorne begonnen wird, wenn die Ergebnisse nicht gleichmäßig über den möglichen Bereich verteilt werden.

function crypto_rand($min,$max,$pedantic=True) { 
    $diff = $max - $min; 
    if ($diff <= 0) return $min; // not so random... 
    $range = $diff + 1; // because $max is inclusive 
    $bits = ceil(log(($range),2)); 
    $bytes = ceil($bits/8.0); 
    $bits_max = 1 << $bits; 
    // e.g. if $range = 3000 (bin: 101110111000) 
    // +--------+--------+ 
    // |....1011|10111000| 
    // +--------+--------+ 
    // bits=12, bytes=2, bits_max=2^12=4096 
    $num = 0; 
    do { 
     $num = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes))) % $bits_max; 
     if ($num >= $range) { 
      if ($pedantic) continue; // start over instead of accepting bias 
      // else 
      $num = $num % $range; // to hell with security 
     } 
     break; 
    } while (True); // because goto attracts velociraptors 
    return $num + $min; 
} 
+0

Ich habe eine Antwort mit einer Anleitung hinzugefügt, wie Sie CryptoLib verwenden können, um dies als Ersatz zu ersetzen. Dies ist viel sicherer (und hat einen effizienteren Code) und es bietet auch eine Wiederholbarkeitsprüfung. – mjsa

+0

@mjsa Während ich sicher bin, dass deine Krypto-Bibliothek großartig ist, ging es bei dieser Frage um die Mechanismen des Codes selbst, und nicht darum, dass jemand eine andere Bibliothek dafür erstellen sollte. – tylerl

+0

Dies ist keine schlechte Lösung. Ich helfe PHP7's 'random_int()' in PHP 5 Projekte zurück zu portieren, und unser Ansatz funktioniert sogar wenn '$ max - $ min> PHP_INT_MAX'. Sie können unsere Bemühungen auf Github unter [random_compat] (https://github.com/paragonie/random_compat) finden, wenn Sie Ihre eigene Perspektive berücksichtigen möchten. –

8

Die manuelle Seite für openssl_random_pseudo_bytes() hat ein Beispiel, das ich glaube, Sie wollen. Sie können einfach bin2hex() am Ausgang von openssl_random_pseudo_bytes() anrufen, um in eine hexadezimale Zahl zu konvertieren, dann hexdec() auf diesen Wert, um in dezimal zu konvertieren.

$rand_num = hexdec(bin2hex(openssl_random_pseudo_bytes($length, $strong))); 

An diesem Punkt können Sie tun, was auch immer Sie wollen, um einen Wert in dem Bereich zu erhalten, den Sie brauchen. Die andere Möglichkeit (Cheater) besteht darin, einen Systembefehl auszuführen, um eine Zufallszahl zu erzeugen - es gibt einige gute Optionen für Zufallszahlengeneratoren für verschiedene Betriebssysteme, die online verfügbar sind.

+0

Dies beantwortet die Frage nicht vollständig, siehe die anderen Antworten unten. – Andrew

1

gut, verwenden Sie einfach auf das Ergebnis der openssl_random_pseudo_bytes hexdec und Sie werden Ihre ganze Zahl erhalten. Es ist so elegant wie es geht :)

print hexdec('45261b8f'); 

> 1160125327 
+0

Beantwortet die ops-Frage nicht, da rand ein Minimum und Maximum annimmt und nur eine Zufallszahl zurückgibt. – nate

+0

Nicht direkt, aber es ist ziemlich trivial, dies hinzuzufügen - wie Sie aus der Lösung von der Op selbst sehen können. –

2

Heres eine Version der oben genannten Lösungen, die nicht rekursive Funktion nicht verwendet ruft:

function secure_rand($min,$max) { 
    $range = $max - $min + 1; 
    if ($range == 0) return $min; 
    $length = (int) (log($range,2)/8) + 1; 
    $max = pow(2, 8 * $length); 
    $num = $max + 1; // Hackish, I know.. 
    while ($num > $max) { 
     $num = hexdec(bin2hex(openssl_random_pseudo_bytes($length,$s))); 
    } 
    return ($num % $range) + $min; 
} 
-1
function ($min,$max){ 
    $range = $max-$min+1; 
    do{ 
     $result = floor($range*(hexdec(bin2hex(openssl_random_pseudo_bytes(4)))/0xffffffff)); 
    } while($result == $range);  
    return $result + $min; 
} 
0

Der einfachste Weg, dies zu tun (und die sicherste von allen Optionen hier) ist Cryptolib zu verwenden, die eine randomInt Funktion hat, die ein Drop-in-Ersatz für rand bietet.

Erster Download Cryptolib aus und kleben Sie es in Ihrem Projekt: https://github.com/IcyApril/CryptoLib

Zwei, in diesem Code fallen. ersetzen path/to/mit dem Verzeichnis von cryptolib.php und min max mit minimalen und maximalen Zahlen:

<?php 
    require_once('path/to/cryptoLib.php'); 

    $min = 1; 
    $max = 5; 

    $randomNum = CryptoLib::randomInt($min, $max); 
?> 

Die Cryptolib vollständige Dokumentation finden Sie unter: https://cryptolib.ju.je/

2

Seit PHP 7 ist jetzt aus, die Der einfachste Weg, dieses Problem zu lösen, besteht darin, alle Instanzen von mt_rand durch random_int zu ersetzen.

(Angenommen, Sie aktualisiert haben, das ist.)