我想生成忘记密码标识。我读我可以通过使用mt_rand(时间戳做到这一点),但有些人说,时间标记可能不是唯一的每一次。所以我的困惑位在这里。我可以使用时间戳和这事吗?
题 什么是生成自定义长度的随机/独特令牌最佳做法?
我知道有很多在这里问身边的问题,但我从不同的人读有不同意见后变得更加混乱。
在PHP中,使用random_bytes()
。原因:您正在寻求获得一个密码提示标记的方式,而且,如果是一次性的登录凭据,那么你确实有一个数据保护(这是 - 整个用户帐户)
因此,该代码将如下:
//$length = 78 etc
$token = bin2hex(random_bytes($length));
更新:这个答案的previous versions指的是uniqid()
,这是不正确,如果有安全性的问题,而不仅仅是唯一性。 uniqid()
基本上只是一些编码microtime()
。有几种简单的方法让您的服务器上microtime()
的准确的预测。攻击者可以发出请求重置密码,然后通过一对夫妇可能令牌的尝试。这也是可能的,如果使用more_entropy,作为额外的熵也同样薄弱。感谢@NikiC并指出这@ScottArciszewski。
欲了解更多详情,请参见
这回答了“最佳随机的要求:
从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/
接受的答案(md5(uniqid(mt_rand(), true))
)的早期版本是不安全的,只提供大约2 ^ 60个可能的输出 - 以及在一个星期左右的时间,一个低预算的攻击者蛮力搜索的范围之内:
mt_rand()
is predictable(且仅加起来熵的31个比特)uniqid()
only adds up to 29 bits of entropymd5()
不会增加熵,它只是它混合确定性由于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)。
在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))
。
我以前的上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在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.
}
这些代码段是不完整的解决方案(我避开了输入验证和架构集成),但它们应该成为什么就做什么的例子。
还可以使用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))));
?>