Ich möchte eine Kennung für ein vergessenes Passwort generieren. Ich habe gelesen, dass ich es mit einem Zeitstempel mit mt_rand () tun kann, aber einige Leute sagen, dass der Zeitstempel möglicherweise nicht jedes Mal eindeutig ist. Also bin ich hier etwas verwirrt. Kann ich damit Zeitstempel verwenden?
Frage
Was ist die beste Vorgehensweise, um zufällige / eindeutige Token mit benutzerdefinierter Länge zu generieren?
Ich weiß, dass hier viele Fragen gestellt werden, aber ich werde verwirrter, nachdem ich unterschiedliche Meinungen von den verschiedenen Leuten gelesen habe.
Antworten:
Verwenden Sie in PHP
random_bytes()
. Grund: Sie suchen nach einer Möglichkeit, ein Kennworterinnerungstoken zu erhalten. Wenn es sich um einmalige Anmeldeinformationen handelt, müssen Sie tatsächlich Daten schützen (dh das gesamte Benutzerkonto).Der Code lautet also wie folgt:
Update : Auf frühere Versionen dieser Antwort wurde verwiesen
uniqid()
und das ist falsch, wenn es um Sicherheit und nicht nur um Einzigartigkeit geht.uniqid()
ist im Wesentlichen nurmicrotime()
mit etwas Codierung. Es gibt einfache Möglichkeiten, genaue Vorhersagen über diemicrotime()
auf Ihrem Server zu erhalten. Ein Angreifer kann eine Anforderung zum Zurücksetzen des Kennworts ausgeben und dann einige wahrscheinliche Token durchgehen. Dies ist auch möglich, wenn more_entropy verwendet wird, da die zusätzliche Entropie ähnlich schwach ist. Vielen Dank an @NikiC und @ScottArciszewski für diesen Hinweis.Weitere Details finden Sie unter
quelle
random_bytes()
nur ab PHP7 verfügbar ist. Für ältere Versionen scheint die Antwort von @yesitsme die beste Option zu sein.$length
? Die ID des Benutzers? Oder was?Dies beantwortet die 'beste zufällige' Anfrage:
Adis Antwort 1 von Security.StackExchange hat eine Lösung dafür:
1. Adi, Montag, 12. November 2018, Celeritas, "Generieren eines nicht bewertbaren Tokens für Bestätigungs-E-Mails", 20. September 13, 7:06 Uhr, https://security.stackexchange.com/a/40314/
quelle
openssl_random_pseudo_bytes($length)
- Unterstützung: PHP 5> = 5.3.0, ....................................... ................... (Verwenden Sie für PHP 7 undrandom_bytes($length)
höher) ...................... .................... (Für PHP unter 5.3 - verwenden Sie PHP nicht unter 5.3)Die frühere Version der akzeptierten Antwort (
md5(uniqid(mt_rand(), true))
) ist unsicher und bietet nur etwa 2 ^ 60 mögliche Ausgaben - im Bereich einer Brute-Force-Suche in etwa einer Woche für einen Low-Budget-Angreifer:mt_rand()
ist vorhersehbar (und addiert nur 31 Entropiebits)uniqid()
addiert nur bis zu 29 Bit Entropiemd5()
fügt keine Entropie hinzu, sondern mischt sie nur deterministischDa ein 56-Bit-DES-Schlüssel in ungefähr 24 Stunden brutal erzwungen werden kann und ein durchschnittlicher Fall ungefähr 59 Bit Entropie haben würde, können wir 2 ^ 59/2 ^ 56 = ungefähr 8 Tage berechnen. Abhängig davon, wie diese Token-Überprüfung implementiert ist, kann es möglich sein, Zeitinformationen praktisch zu verlieren und auf die ersten N Bytes eines gültigen Reset-Tokens zu schließen .
Da es sich bei der Frage um "Best Practices" handelt und mit ...
... können wir daraus schließen, dass dieses Token implizite Sicherheitsanforderungen hat. Wenn Sie einem Zufallszahlengenerator Sicherheitsanforderungen hinzufügen, empfiehlt es sich, immer einen kryptografisch sicheren Pseudozufallszahlengenerator (abgekürzt CSPRNG) zu verwenden.
Verwenden eines CSPRNG
In PHP 7 können Sie verwenden
bin2hex(random_bytes($n))
(wobei$n
eine Ganzzahl größer als 15 ist).In PHP 5 können Sie
random_compat
dieselbe API verfügbar machen.Alternativ,
bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))
wenn Sieext/mcrypt
installiert haben. Ein weiterer guter Einzeiler istbin2hex(openssl_random_pseudo_bytes($n))
.Trennen der Suche vom Validator
Aus meiner früheren Arbeit an sicheren "Remember Me" -Cookies in PHP besteht die einzige wirksame Möglichkeit, das oben erwähnte Timing-Leck (das normalerweise durch die Datenbankabfrage verursacht wird) zu verringern, darin, die Suche von der Validierung zu trennen.
Wenn Ihre Tabelle so aussieht (MySQL) ...
... Sie müssen eine weitere Spalte hinzufügen
selector
, wie folgt:Verwenden eines CSPRNG Wenn ein Token zum Zurücksetzen des Kennworts ausgegeben wird, senden Sie beide Werte an den Benutzer, speichern Sie den Selektor und einen SHA-256-Hash des zufälligen Tokens in der Datenbank. Verwenden Sie den Selektor, um den Hash und die Benutzer-ID abzurufen, und berechnen Sie den SHA-256-Hash des Tokens, den der Benutzer mit dem in der Datenbank gespeicherten Token bereitstellt
hash_equals()
.Beispielcode
Generieren eines Reset-Tokens in PHP 7 (oder 5.6 mit random_compat) mit PDO:
Überprüfen des vom Benutzer bereitgestellten Reset-Tokens:
Diese Codefragmente sind keine vollständigen Lösungen (ich habe die Eingabevalidierung und die Framework-Integration vermieden), aber sie sollten als Beispiel dafür dienen, was zu tun ist.
quelle
hash('sha256', bin2hex($token))
, 2) mit zu verifizierenif (hash_equals(hash('sha256', $validator), $results[0]['token'])) {...
? Vielen Dank!id
als Selektor verwenden? Ich meine, der Primärschlüssel deraccount_recovery
Tabelle. Wir brauchen keine zusätzliche Sicherheitsebene für den Selektor, oder? Vielen Dank!id:secret
ist in Ordnung.selector:secret
ist in Ordnung.secret
selbst ist nicht. Das Ziel besteht darin, die Datenbankabfrage (die zeitlich undicht ist) vom Authentifizierungsprotokoll (das eine konstante Zeit sein sollte) zu trennen.openssl_random_pseudo_bytes
statt ,random_bytes
wenn läuft PHP 5.6? Sollten Sie nicht auch nur den Selektor und nicht den Validator an den Querystring des Links anhängen?Sie können auch DEV_RANDOM verwenden, wobei 128 = 1/2 der generierten Tokenlänge ist. Der folgende Code generiert 256 Token.
quelle
MCRYPT_DEV_URANDOM
über vorschlagenMCRYPT_DEV_RANDOM
.Dies kann hilfreich sein, wenn Sie ein sehr, sehr zufälliges Token benötigen
quelle