Generieren kryptografisch sicherer Token

83

Um ein 32-stelliges Token für den Zugriff auf unsere API zu generieren, verwenden wir derzeit:

$token = md5(uniqid(mt_rand(), true));

Ich habe gelesen, dass diese Methode nicht kryptografisch sicher ist, da sie auf der Systemuhr basiert, und das openssl_random_pseudo_byteswäre eine bessere Lösung, da es schwieriger vorherzusagen wäre.

Wenn dies der Fall ist, wie würde der entsprechende Code aussehen?

Ich nehme so etwas an, aber ich weiß nicht, ob das richtig ist ...

$token = md5(openssl_random_pseudo_bytes(32));

Auch welche Länge macht Sinn, dass ich an die Funktion übergeben soll?

Feuer
quelle
4
Warum md5 es aber? Konvertieren Sie einfach den Bytestream in hex: Sie erhalten 32 Bytes von openssl_random_pseudo_bytes () zurück. Rendern Sie jedes dieser Bytes mit bin2hex () als Hex-Wert, wie in den Beispielen für PHP-Dokumente gezeigt
Mark Baker,
Ich möchte nur 32 Zeichen? Wie würde ich das machen?
Feuer
10
md5()generiert eine 32-stellige Zeichenfolge, enthält jedoch nur Daten im Wert von 128 Bit. openssl_random_pseudo_bytes()Gibt echte Binärdaten zurück, hat also 32 * 8 = 256 Bit Zufälligkeit. Indem Sie Ihre 32-Byte-Zufallszeichenfolge durch md5 stopfen, reduzieren Sie effektiv ihre Einzigartigkeit um einen massiven Betrag.
Marc B
1
Ist das also $token = bin2hex(openssl_random_pseudo_bytes(16));ausreichend oder brauche ich eine Schleife von 16 Iterationen, die 1 als Länge übergeben und hexadezimal an eine Zeichenfolge anhängen?
Feuer
Die endgültige Lösung mit 16 in hexadezimal konvertierten Bytes ist korrekt. Aber Sie sollten sich hier nicht wirklich auf OpenSSL verlassen # 1 # 2 # 3 . Sobald Sie PHP 7.0+ verwenden, gibt es wirklich keine Entschuldigung mehr, es zu verwenden. Versuchen Sie es mit OpenSSL und Hex-Codierung, Random::alphanumericString($length)die ungefähr 2 Milliarden Mal so viel „Entropie“ in diese 16 Zeichen passt.
Caw

Antworten:

187

Hier ist die richtige Lösung:

$token = bin2hex(openssl_random_pseudo_bytes(16));

# or in php7
$token = bin2hex(random_bytes(16));
Feuer
quelle
1
Ich habe eine weitere Antwort hinzugefügt, wie Sie mit openssl_random_pseudo_bytes mit CryptoLib ein eindeutiges Token generieren können. Auf diese Weise können Sie Token mit allen Zeichen generieren, im Gegensatz zu nur 0-9, AF.
mjsa
4
PHP 5.x Unterstützung für random_bytes () und random_int () github.com/paragonie/random_compat
MTK
4
Vielleicht möchten bin2hex(random_bytes($length/2))Sie auch, weil Sie 2 Hex-Zeichen für jedes Byte haben können, so dass die Anzahl der Zeichen $lengthohne es immer doppelt ist
Ein Freund
3
@AFriend Einverstanden. Wenn Sie eine Funktion erstellen möchten, die eine Zeichenfolge mit einer $lengthAnzahl von Zeichen erzeugt (anstatt sich um die Bytegröße zu kümmern), möchten Sie übergeben random_bytes($length/2).
BadHorsie
10

Wenn Sie openssl_random_pseudo_bytes verwenden möchten, verwenden Sie am besten die CrytoLib-Implementierung. Auf diese Weise können Sie alle alphanumerischen Zeichen generieren. Wenn Sie bin2hex um openssl_random_pseudo_bytes kleben, erhalten Sie nur AF (Großbuchstaben) und Zahlen.

Ersetzen Sie den Pfad / zu / durch den Speicherort der Datei cryptolib.php (Sie finden ihn auf GitHub unter: https://github.com/IcyApril/CryptoLib ).

<?php
  require_once('path/to/cryptolib.php');
  $token = IcyApril\CryptoLib::randomString(16);
?>

Die vollständige CryptoLib-Dokumentation finden Sie unter: https://cryptolib.ju.je/ . Es behandelt viele andere zufällige Methoden, die kaskadierende Verschlüsselung sowie die Generierung und Validierung von Hashs. aber es ist da, wenn Sie es brauchen.

mjsa
quelle
Für die gleiche Anzahl von Bytes: openssl_random_pseudo_bytes(16)Jedes Byte kann einen beliebigen Wert (0-255) haben, während randomString(16)jedes Byte nur az Az 0-9 haben kann, was 62 Kombinationen pro Byte entspricht. Yup randomString ist pro Zeichenfolgenlänge besser , da bin2hex eine ineffiziente Codierung ist (50%). Besser base64_encode(openssl_random_pseudo_bytes(16))für eine kürzere Zeichenfolge und eine genau bekannte Anzahl von Sicherheitsbytes (16 in diesem Fall, aber nach Bedarf ändern)
Rodney
1
@Rodney Bedenken Basis 64 produzieren auch einen String mit +, /und =Zeichen, also , wenn Sie versuchen , ein benutzerfreundliches Token zu generieren (zB einen API - Schlüssel) ist es nicht ideal ist.
BadHorsie
9

Wenn Sie einen kryptografisch sicheren Zufallszahlengenerator haben, müssen Sie dessen Ausgabe nicht hashen. Tatsächlich willst du nicht. Benutz einfach

$token  = openssl_random_pseudo_bytes($BYTES,true)

Wobei $ BYTES wie viele Datenbytes Sie möchten. MD5 hat einen 128-Bit-Hash, also reichen 16 Bytes.

Nebenbei bemerkt, keine der Funktionen, die Sie in Ihrem Originalcode aufrufen, ist kryptografisch sicher. Die meisten sind so schädlich, dass die Verwendung nur einer Funktion unsicher wäre, selbst wenn sie mit sicheren anderen Funktionen kombiniert wird. MD5 weist Sicherheitsprobleme auf (obwohl diese für diese Anwendung möglicherweise nicht relevant sind). Uniqid generiert nicht nur standardmäßig keine kryptografisch zufälligen Bytes (da es die Systemuhr verwendet), sondern die hinzugefügte Entropie, die Sie übergeben, wird mithilfe eines linearen kongruenten Generators kombiniert, der nicht kryptografisch sicher ist. In der Tat bedeutet dies wahrscheinlich, dass Sie alle Ihre API-Schlüssel erraten können, wenn Sie Zugriff auf einige von ihnen haben, selbst wenn sie keine Ahnung vom Wert Ihrer Serveruhr hatten. Schließlich ist mt_rand (), was Sie als zusätzliche Entropie verwenden, auch kein sicherer Zufallszahlengenerator.

Imichaelmiers
quelle
27
Das zweite Argument für openssl_random_pseudo_bytes () sollte eine Variable sein, die als Referenz übergeben wird. Nach openssl_random_pseudo_bytes ist diese Variable wahr oder falsch, wenn openssl einen kryptografisch starken Algorithmus verwenden konnte. Es wird nicht verwendet, um openssl anzuweisen, einen kryptografisch starken Algorithmus zu verwenden, was es sowieso versuchen wird.
Jonathan Amend
-1

Zuverlässige Passwörter können Sie nur aus machen ascii Zeichen a-zA-Z und die Zahlen 0-9 . Um dies zu erreichen, verwenden Sie am besten nur kryptografisch sichere Methoden wie random_int () oder random_bytes () aus PHP7. Restfunktionen als base64_encode () Sie können nur als Unterstützungsfunktionen verwenden, um die Zuverlässigkeit der Zeichenfolge zu erhöhen und sie in ASCII- Zeichen zu ändern .

mt_rand () ist nicht sicher und sehr alt. Aus einer beliebigen Zeichenfolge Sie müssen random_int () verwenden. Von der Binärzeichenfolge Sie sollten base64_encode () verwenden , um die Binärzeichenfolge zuverlässig zu machen, oder bin2hex, aber dann werden Sie das Byte nur auf 16 Positionen (Werte) schneiden. Siehe meine Implementierung dieser Funktionen. Es werden alle Sprachen verwendet.

Daro aus Polen
quelle