首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为忘记密码生成随机令牌的最佳实践

为忘记密码生成随机令牌的最佳实践
EN

Stack Overflow用户
提问于 2013-09-20 15:06:56
回答 4查看 136.5K关注 0票数 99

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

问题

生成自定义长度的随机/唯一令牌的最佳实践是什么?

我知道这里有很多问题,但在阅读了不同人的不同意见后,我变得更加困惑。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-09-20 15:14:17

在PHP中,使用random_bytes()。原因:您正在寻找获取密码提醒令牌的方法,如果它是一次性登录凭据,那么您实际上有一个数据需要保护(即整个用户帐户)

因此,代码如下所示:

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

更新:这个答案的指的是uniqid(),如果存在安全问题,而不仅仅是唯一性问题,那么这是不正确的。uniqid()本质上只是一些编码的microtime()。有一些简单的方法可以获得服务器上microtime()的准确预测。攻击者可以发出密码重置请求,然后尝试破解几个可能的令牌。如果使用more_entropy,这也是可能的,因为附加熵同样很弱。感谢和指出这一点。

有关更多详细信息,请参阅

票数 156
EN

Stack Overflow用户

发布于 2015-07-15 07:47:32

早期版本的accepted (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

因为这个问题是关于“最佳实践”的,所以开头是...

我想为忘记密码生成标识符

...we可以推断该令牌具有隐含的安全要求。当您向随机数生成器添加安全需求时,最佳实践是始终使用加密安全的伪随机数生成器(缩写为CSPRNG)。

使用CSPRNG

在PHP7中,可以使用bin2hex(random_bytes($n)) (其中$n是大于15的整数)。

在PHP5中,您可以使用random_compat来公开相同的API。

或者,如果安装了ext/mcrypt,则选择bin2hex(mcrypt_create_iv($n, MCRYPT_DEV_URANDOM))。另一个很好的一行程序是bin2hex(openssl_random_pseudo_bytes($n))

将查找与验证器分开

根据我以前在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散列。

示例代码

使用PDO在PHP7(或random_compat的5.6 )中生成重置令牌:

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

这些代码片段并不是完整的解决方案(我避开了输入验证和框架集成),但它们应该作为执行操作的示例。

票数 56
EN

Stack Overflow用户

发布于 2013-11-22 02:13:59

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

$token = bin2hex(mcrypt_create_iv(128, MCRYPT_DEV_RANDOM));
票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18910814

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档