最佳实践来生成忘记密码随机令牌

问题描述 投票:83回答:6

我想生成忘记密码标识。我读我可以通过使用mt_rand(时间戳做到这一点),但有些人说,时间标记可能不是唯一的每一次。所以我的困惑位在这里。我可以使用时间戳和这事吗?

题 什么是生成自定义长度的随机/独特令牌最佳做法?

我知道有很多在这里问身边的问题,但我从不同的人读有不同意见后变得更加混乱。

php security random timestamp token
6个回答
138
投票

在PHP中,使用random_bytes()。原因:您正在寻求获得一个密码提示标记的方式,而且,如果是一次性的登录凭据,那么你确实有一个数据保护(这是 - 整个用户帐户)

因此,该代码将如下:

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

更新:这个答案的previous versions指的是uniqid(),这是不正确,如果有安全性的问题,而不仅仅是唯一性。 uniqid()基本上只是一些编码microtime()。有几种简单的方法让您的服务器上microtime()的准确的预测。攻击者可以发出请求重置密码,然后通过一对夫妇可能令牌的尝试。这也是可能的,如果使用more_entropy,作为额外的熵也同样薄弱。感谢@NikiC并指出这@ScottArciszewski

欲了解更多详情,请参见


70
投票

这回答了“最佳随机的要求:

从Security.StackExchange Adi's answer1有一个解决方案:

请确保您有OpenSSL的支持,你永远不会出错,这一个班轮

$token = bin2hex(openssl_random_pseudo_bytes(16));

1.访问11月12日2018年,速度,“生成用于确认电子邮件的不可猜测令牌”,9月20日在'13 7:06,https://security.stackexchange.com/a/40314/


54
投票

接受的答案(md5(uniqid(mt_rand(), true)))的早期版本是不安全的,只提供大约2 ^ 60个可能的输出 - 以及在一个星期左右的时间,一个低预算的攻击者蛮力搜索的范围之内:

由于56-bit DES key can be brute-forced in about 24 hours,平均情况下将具有约59的熵比特,我们可以计算出2 ^ 59/2 ^ 56 =约8天。根据此令牌验证是如何实现的,it might be possible to practically leak timing information and infer the first N bytes of a valid reset token

由于问题是关于“最佳做法”,并打开...

我想生成忘记密码标识

......我们可以推断,此标记具有隐含的安全要求。而当你添加的安全要求,以一个随机数发生器,最好的做法是始终使用密码安全伪随机数发生器(简称CSPRNG)。


Using a 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))

Separating the Lookup from the Validator

我以前的上secure "remember me" cookies in 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散列。使用选择器抓取的散列和用户ID,计算出用户与存储在使用hash_equals()数据库中的一个提供的令牌的SHA-256哈希值。

Example Code

生成与PDO在PHP 7复位令牌(或5.6与random_compat):

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

这些代码段是不完整的解决方案(我避开了输入验证和架构集成),但它们应该成为什么就做什么的例子。


6
投票

还可以使用DEV_RANDOM,其中128 = 1/2生成的令牌长度。下面的代码生成256令牌。

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

1
投票

每当你需要一个非常非常随机令牌,这可能是有益的

<?php
   echo mb_strtoupper(strval(bin2hex(openssl_random_pseudo_bytes(16))));
?>
© www.soinside.com 2019 - 2024. All rights reserved.