najlepsza praktyka generowania losowego tokena dla zapomnianego hasła

Chcę wygenerować identyfikator dla przypomnienia hasła . Czytałem, że mogę to zrobić używając timestamp z mt_rand (), ale niektórzy mówią, że znacznik czasu może nie być unikalny za każdym razem. Jestem trochę zdezorientowany. Czy Mogę to zrobić za pomocą znacznika czasu z tym ?

Pytanie
Jaka jest najlepsza praktyka generowania losowych / unikalnych żetonów o niestandardowej długości?

Wiem, że jest tu wiele pytań, ale jestem coraz bardziej zdezorientowany po przeczytaniu różnych opinie różnych ludzi.

Author: Scott Arciszewski, 2013-09-20

6 answers

W PHP, użyj random_bytes(). Powód: szukasz sposobu, aby uzyskać token przypomnienia hasła, a jeśli jest to jednorazowe poświadczenia logowania, to faktycznie masz dane do ochrony (czyli całe konto użytkownika) {]}

Więc kod będzie następujący:

//$length = 78 etc
$token = bin2hex(random_bytes($length));

Aktualizacja: poprzednie wersje tej odpowiedzi odnosiły się do {[2] } i jest to nieprawidłowe, jeśli chodzi o bezpieczeństwo, a nie tylko wyjątkowość. uniqid() jest w zasadzie tylko microtime() z jakieś kodowanie. Istnieją proste sposoby na uzyskanie dokładnych prognoz microtime() na twoim serwerze. Atakujący może wystosować żądanie zresetowania hasła, a następnie wypróbować kilka prawdopodobnych tokenów. Jest to również możliwe w przypadku użycia more_entropy, ponieważ dodatkowa Entropia jest podobnie słaba. Podziękowania dla @NikiC i @ScottArciszewski za wskazanie tego.

Po Więcej szczegółów zobacz

 127
Author: Alma Do,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:24

Https://security.stackexchange.com/questions/40310/generating-an-unguesable-token-for-confirmation-e-mails

To odpowiedzi najlepiej losowe

$token = bin2hex(openssl_random_pseudo_bytes(16));
 57
Author: yesitsme,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-17 10:45:55

W przeciwieństwie do poprzednich wersji, nie są one dostępne dla osób, które nie mają dostępu do Internetu i nie mają dostępu do Internetu, ale są dostępne dla osób, które nie mają dostępu do Internetu.]}

Od 56-bitowego klucza DES może być brutalnie wymuszony w około 24 godziny, a przeciętny przypadek miałby około 59 bitów entropii, możemy obliczyć 2^59 / 2^56 = około 8 dni. W zależności od tego, jak ta weryfikacja tokenu jest zaimplementowana, możliwe jest praktycznie wyciek informacji o czasie i wnioskowanie pierwszych N bajtów poprawnego tokenu resetowania.

Ponieważ pytanie dotyczy "najlepszych praktyk" i otwiera się od...

Chcę wygenerować identyfikator dla forgot hasło

...możemy wywnioskować, że ten token ma ukryte wymagania bezpieczeństwa. Jeśli dodasz wymagania bezpieczeństwa do generatora liczb losowych, najlepszą praktyką jest zawsze używanie zabezpieczonego kryptograficznie generatora liczb pseudorandomowych (W skrócie CSPRNG).


Użycie CSPRNG

W PHP 7 możesz użyć bin2hex(random_bytes($n)) (gdzie $n jest liczbą całkowitą większą niż 15).

W PHP 5 możesz użyć random_compat aby odsłonić to samo API.

Alternatywnie, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM)) Jeśli masz ext/mcrypt zainstalowany. Innym dobrym jednowierszowcem jest bin2hex(openssl_random_pseudo_bytes($n)).

Oddzielenie Wyszukiwania od walidatora

Wyciągając z mojej poprzedniej pracy nad bezpiecznymi Plikami cookie "remember me" w PHP , jedynym skutecznym sposobem złagodzenia wspomnianego wcześniej wycieku czasowego (zazwyczaj wprowadzanego przez zapytanie do bazy danych) jest oddzielenie Wyszukiwania od walidacji.

Jeśli twoja tabela wygląda tak (MySQL)...

CREATE TABLE account_recovery (
    id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT 
    userid INTEGER(11) UNSIGNED NOT NULL,
    token CHAR(64),
    expires DATETIME,
    PRIMARY KEY(id)
);

... musisz dodaj jeszcze jedną kolumnę, selector, tak:

CREATE TABLE account_recovery (
    id INTEGER(11) UNSIGNED NOT NULL AUTO_INCREMENT 
    userid INTEGER(11) UNSIGNED NOT NULL,
    selector CHAR(16),
    token CHAR(64),
    expires DATETIME,
    PRIMARY KEY(id),
    KEY(selector)
);

Użyj CSPRNG, gdy zostanie wydany token resetowania hasła, wyślij obie wartości do użytkownika, Zapisz Selektor i hash SHA-256 losowego tokena w bazie danych. Użyj selektora, aby pobrać hash i identyfikator użytkownika, Oblicz hash SHA-256 tokenu dostarczanego przez użytkownika z tym przechowywanym w bazie danych za pomocą hash_equals().

Przykładowy Kod

Generowanie tokena resetowego w PHP 7 (lub 5.6 z random_compat) z CHNP:

$selector = bin2hex(random_bytes(8));
$token = random_bytes(32);

$urlToEmail = 'http://example.com/reset.php?'.http_build_query([
    'selector' => $selector,
    'validator' => bin2hex($token)
]);

$expires = new DateTime('NOW');
$expires->add(new DateInterval('PT01H')); // 1 hour

$stmt = $pdo->prepare("INSERT INTO account_recovery (userid, selector, token, expires) VALUES (:userid, :selector, :token, :expires);");
$stmt->execute([
    'userid' => $userId, // define this elsewhere!
    'selector' => $selector,
    'token' => hash('sha256', $token),
    'expires' => $expires->format('Y-m-d\TH:i:s')
]);

Weryfikacja tokena resetowego podanego przez użytkownika:

$stmt = $pdo->prepare("SELECT * FROM account_recovery WHERE selector = ? AND expires >= NOW()");
$stmt->execute([$selector]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($results)) {
    $calc = hash('sha256', hex2bin($validator));
    if (hash_equals($calc, $results[0]['token'])) {
        // The reset token is valid. Authenticate the user.
    }
    // Remove the token from the DB regardless of success or failure.
}

Te fragmenty kodu nie są kompletnymi rozwiązaniami( zrezygnowałem z walidacji wejściowej i integracji frameworków), ale powinny służyć jako przykład tego, co należy zrobić.

 50
Author: Scott Arciszewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-26 04:38:23

Możesz również użyć DEV_RANDOM, gdzie 128 = 1/2 długości wygenerowanego tokena. Poniższy kod generuje 256 tokenów.

$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
 6
Author: Graham T,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-21 19:36:59

To może być pomocne, gdy potrzebujesz bardzo bardzo losowego tokena

<?php
   echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
 1
Author: Ir Calif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-22 11:22:40

Możesz użyć

echo str_shuffle('ASGDHFfdgfdre5475433fd');
 -6
Author: saif,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-08 00:20:38