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

2020/10/10 14:01 · php ·  · 0评论

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

问题

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

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

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

因此,代码将如下所示:

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

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

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

这回答了“最佳随机”请求:

Adi来自Security.StackExchange的答案1为此提供了一个解决方案:

确保您具有OpenSSL支持,并且这种一线式操作绝对不会出错

$token = bin2hex(openssl_random_pseudo_bytes(16));

1. Adi,2018年11月12日星期一,Celeritas,“为确认电子邮件生成无法猜测的令牌”,2013年9月20日,7:06,https: //security.stackexchange.com/a/40314/

可接受的答案(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))

将查找与验证器分开

从我以前在PHP中使用安全的“记住我” cookie的工作中汲取经验,减轻上述定时泄漏(通常由数据库查询引入)的唯一有效方法是将查找与验证分开。

如果您的表格如下所示(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,使用计算用户提供的令牌与数据库中存储的令牌的SHA-256哈希hash_equals()

范例程式码

使用PDO在PHP 7(或带有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.
}

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

您还可以使用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))));
?>
本文地址:http://php.askforanswer.com/shengchengsuijilingpaiyiwangjimimadezuijiashijian.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 admin 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!