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


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

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

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

6 80

6 ответов:

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

таким образом, код будет выглядеть следующим образом:

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

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

Подробнее см.

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

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

$token = bin2hex(openssl_random_pseudo_bytes(16));

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

С a 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 "remember me" в 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.
}

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

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

$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));

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

<?php
   echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>

можно использовать

echo str_shuffle('ASGDHFfdgfdre5475433fd');