生成用于创建密码检索令牌的随机“站点盐”的好方法是啥?

Posted

技术标签:

【中文标题】生成用于创建密码检索令牌的随机“站点盐”的好方法是啥?【英文标题】:What is a good way to produce a random "site salt" to be used in creating password retrieval tokens?生成用于创建密码检索令牌的随机“站点盐”的好方法是什么? 【发布时间】:2011-03-18 10:40:51 【问题描述】:

我想创建一个站点范围的哈希,用作创建密码检索令牌的盐。我一直在 *** 周围蹦蹦跳跳,试图了解做到这一点的最佳方法。

这是重置过程:

当用户请求密码重置电子邮件时,代码会生成一个检索令牌:

$token = hash_hmac('sha256', $reset_hash* , $site_hash)

*$reset_hash 是使用 phpass HashPassword() 函数创建的哈希,保存在用户表中。

然后,我将 URL 中的令牌发送到用户的电子邮件地址。他们在令牌在一小时内超时之前单击。我将他们的提交与服务器端生成的质询令牌相匹配。如果匹配,则强制他们选择新密码,然后登录。

我想知道生成 $site_key 的最佳方法。我正在考虑使用另一个由随机数播种的 HMAC 哈希:

$site_key = hash_hmac('sha256', MCRYPT_DEV_RANDOM, MCRYPT_DEV_RANDOM);

这会产生这样的结果:

98bb403abbe62f5552f03494126a732c3be69b41401673b08cbfefa46d9e8999

这将是一个适当的随机用于此目的吗?我是不是把它复杂化了,还是以错误的方式接近它?

I was inspired to use HMAC by this answer

编辑:我试图避免一些同事敦促的“秘密问题”步骤,所以我希望重置链接提供一个重置密码的步骤。因此,我担心这个过程足够安全,可以保护包含敏感信息的系统。

暂时解决:我将使用 The Rook 所描述的随机数作为重置令牌。感谢大家的 cmets 和反馈。

【问题讨论】:

我不得不说这太复杂了。我使用一个简单的 6 字符随机字母数字令牌,像你说的那样有超时,以及基本的暴力检测。 ***.com/questions/1416060/… # 阅读这个,它应该涵盖了你需要理解的大部分概念。 @Fosco:基本暴力检测是指限制在允许的重置时间内可以验证令牌的次数吗? @Kent:所以你认为站点范围的盐是一个坏主意,而不是为了密码重置而保存临时盐?我已经使用经过审查的散列算法 (phpass) 保护密码,该算法包含每个用户的随机盐。我认为这充分保护了密码。我不太确定我的密码重置程序。 @Todd:我记录了来自某个 IP 地址的身份验证尝试次数,并在设定的限制后开始丢弃它们。这个想法是,在蛮力下,他们不会拥有令牌并且会试图得到它。不太可能,但有时你必须尝试。这里的另一种可能性是只使用 GUID/UUID 而忘记其他所有内容。 【参考方案1】:

首先,您不是在谈论盐。你说的是Cryptographic Nonce,当你加盐密码时,你应该使用加密随机数。在重置密码的情况下,它应该是存储在数据库中的随机数。有一个“站点盐”是不利的。

首先,我不喜欢uniqid(),因为这是一个耗时的计算和time is a very weak seed。 rand() vs mt_rand(),剧透:rand() 完全是废话。

在 Web 应用程序中,安全机密的良好来源是对熵池(例如 /dev/urandom)的非阻塞访问。从 PHP 5.3 开始,PHP 应用程序可以使用openssl_random_pseudo_bytes(),Openssl 库将根据您的操作系统选择最佳熵源,在 Linux 下这意味着应用程序将使用/dev/urandom。这个code snip from Scott is pretty good:

function crypto_rand_secure($min, $max) 
        $range = $max - $min;
        if ($range < 0) return $min; // not so random...
        $log = log($range, 2);
        $bytes = (int) ($log / 8) + 1; // length in bytes
        $bits = (int) $log + 1; // length in bits
        $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
        do 
            $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
            $rnd = $rnd & $filter; // discard irrelevant bits
         while ($rnd >= $range);
        return $min + $rnd;


function getToken($length=32)
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    for($i=0;$i<$length;$i++)
        $token .= $codeAlphabet[crypto_rand_secure(0,strlen($codeAlphabet))];
    
    return $token;

【讨论】:

@TheRook:谢谢。您是否建议使用 $password_token 作为完整令牌。使用 nonce 来加盐 $reset_hash 来创建一个不存储在数据库中但在用户单击电子邮件中的重置 URL 时可验证的令牌会更好吗?这又是不是有点过头了?我试图避免我的一些同事敦促的“秘密问题”步骤,所以我希望重置链接提供一个重置密码的步骤。 我讨厌“秘密问题”的东西,通常它是让我对您的网站“不再感兴趣”的事情之一。但我总是偏执,有人会劫持我的电子邮件帐户,然后重置我所有的密码....你赢不了。另外,尝试支持 OpenID?太棒了! 最好对 URL 使用修改后的 Base-64 字符集(参见 tools.ietf.org/html/rfc4648#section-5)。 @Todd Holmberg 采用随机数的哈希值并不会使其更加随机,只需使用该值即可。 @Gumbo,@TheRook 在 base64_encode() 页面的 cmets 中找到此内容:$output = strtr(base64_encode($input), '+/=', '-_,')

以上是关于生成用于创建密码检索令牌的随机“站点盐”的好方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

生成重置密码令牌的最佳实践

如何创建密码重置链接?

JWT 令牌登录和注销

如何从 ASP.NET Core 中的密码重置令牌中检索用户?

从Mysql数据库中自动删除

为 JWT 生成密钥?