рекомендуется генерировать случайный токен для забытого пароля


Я хочу сгенерировать идентификатор для забытого пароля. Я читал, что могу сделать это, используя метку времени с помощью mt_rand(), но некоторые люди говорят, что метка времени может не быть уникальной каждый раз. Так что я немного запутался здесь. Могу ли я сделать это, используя отметку времени с этим?

Вопрос
Как лучше всего генерировать случайные/уникальные токены произвольной длины?

Я знаю, что здесь задают много вопросов, но я все больше запутываюсь после прочтения разных мнение разных людей.

Author: Scott Arciszewski, 2013-09-20

6 answers

В PHP используйте random_bytes(). Причина: вы ищете способ получить токен напоминания пароля, и, если это одноразовые учетные данные для входа, то у вас действительно есть данные для защиты (то есть - вся учетная запись пользователя)

Итак, код будет следующим:

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

Обновление: предыдущие версии этого ответа ссылались на uniqid(), и это неверно, если речь идет о безопасности, а не только об уникальности. uniqid() по сути, это просто microtime() с некоторой кодировкой. Существуют простые способы получить точные прогнозы microtime() на вашем сервере. Злоумышленник может отправить запрос на сброс пароля, а затем попробовать использовать несколько вероятных токенов. Это также возможно, если используется больше энтропии, так как дополнительная энтропия также слаба. Спасибо @NikiC и @Scottarciszewski за указание на это.

Для получения более подробной информации см.

 127
Author: Alma Do, 2017-05-23 11:33:24

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

Это лучший случайный ответ

$token = bin2hex(openssl_random_pseudo_bytes(16));
 57
Author: yesitsme, 2017-03-17 10:45:55

Более ранняя версия принятого ответа (md5(uniqid(mt_rand(), true))) небезопасна и предлагает только около 2^60 возможных выходов - вполне в пределах досягаемости поиска грубой силы примерно через неделю для малобюджетного злоумышленника:

Начиная с 56-разрядного DES ключ может быть принудительно введен примерно за 24 часа, и в среднем случае будет около 59 бит энтропии, мы можем рассчитать 2^59/2^56 = около 8 дней. В зависимости от того, как реализована эта проверка токена, может оказаться возможным практически утечка информации о времени и вывод первых N байтов допустимого токена сброса.

Поскольку вопрос касается "лучших практик" и начинается с...

Я хочу сгенерировать идентификатор для забытого пароль

...мы можем сделать вывод, что этот токен имеет неявные требования к безопасности. И когда вы добавляете требования безопасности к генератору случайных чисел, лучше всего всегда использовать криптографически безопасный генератор псевдослучайных чисел (сокращенно CSPRNG).


Использование CSPRNG

В PHP 7 вы можете использовать bin2hex(random_bytes($n)) (где $n - целое число, большее 15).

В PHP 5 вы можете использовать random_compat чтобы предоставить тот же API.

В качестве альтернативы, bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM)), если у вас установлен ext/mcrypt. Еще один хороший однострочный - это bin2hex(openssl_random_pseudo_bytes($n)).

Отделение поиска от средства проверки

Исходя из моей предыдущей работы над безопасными файлами cookie "запомни меня" в PHP, единственный эффективный способ уменьшить вышеупомянутую утечку времени (обычно возникающую при запросе базы данных) - это отделить поиск от проверки.

Если ваша таблица выглядит так (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)
);

... вам нужно добавьте еще один столбец, selector, вот так:

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)
);

Используйте CSPRNG, когда выдается токен сброса пароля, отправьте оба значения пользователю, сохраните селектор и хэш SHA-256 случайного токена в базе данных. Используйте селектор для захвата хэша и идентификатора пользователя, вычислите хэш SHA-256 токена, который пользователь предоставляет вместе с тем, который хранится в базе данных, используя hash_equals().

Пример кода

Генерация токена сброса в PHP 7 (или 5.6 с помощью random_compat) с помощью PDO:

$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')
]);

Проверка предоставленного пользователем токена сброса:

$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.
}

Эти фрагменты кода не являются полными решениями (я отказался от проверки ввода и интеграции фреймворка), но они должны служить примером того, что нужно делать.

 50
Author: Scott Arciszewski, 2016-06-26 04:38:23

Вы также можете использовать DEV_RANDOM, где 128 = 1/2 длины сгенерированного токена. Приведенный ниже код генерирует 256 токенов.

$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
 6
Author: Graham T, 2013-11-21 19:36:59

Это может быть полезно всякий раз, когда вам нужен очень, очень случайный токен

<?php
   echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
 1
Author: Ir Calif, 2017-05-22 11:22:40

Вы можете использовать

echo str_shuffle('ASGDHFfdgfdre5475433fd');
 -6
Author: saif, 2018-08-08 00:20:38