我目前是开发阶段项目的工程师。此项目上的一个“模块”提供了用户身份验证/授权的功能。但是,引起我们关注的是,密码哈希算法可能无法满足cop(又不是BCrypt)的要求。 (可怕的事情不是很确定它是什么,它是从哪里来的!)。

这显然必须更改,并且补丁程序正在计划中。我们必须自然地更新所有测试用户,因为他们的密码将使用旧的哈希方法,这不是什么大问题,我们所有的演示用户都是自动构建的,因此可以更新脚本。但是接下来的问题是,如果这是一个拥有大量活跃用户和陈旧用户的生产系统,该怎么办?最佳做法是什么。


自动对每个用户强制重置密码?这将通知每个用户其密码已更改,并且可能引起问题/混乱,并可能导致怀疑存在安全漏洞。可能会询问更多问题,网站涉众可能不一定能够回答。
更新数据库以标记是新方法还是旧方法,然后在验证用户身份后使用来更新数据库中的密码。在服务中需要一点逻辑,对于任何现有用户而言,过渡都是显而易见的。问题在于,是否存在违规行为,那么很可能这里发生了两种方法,如果发现一种不太安全的方法,那就是显然可以将其破解。
使用BCrypted重置所有密码。现有哈希的版本。将其标记为旧样式,因此在成功进行身份验证时,它仅保留密码的哈希而不是哈希的哈希。


评论

我赞成“主要基于意见”的投票,在这里要说的是扎实客观的东西。

旧算法是可逆的吗?暂时忽略任何道德问题;如果旧的旧算法已被破坏并且可以理解,足以让您恢复原始密码,那么就可以在用户不登录的情况下将用户记录更新为新算法,而明智的选择是。只是一个想法!

经过仔细检查。它似乎正在使用PBKDF2,这似乎是完全可以接受的(有问题的开发人员仍在本周剩余时间内购买蛋糕,因为这并不明显!)。我仍然认为我的大部分问题(“更改密码存储哈希值时该怎么办”)对其他用户将来有效。

只是大声思考:定义过渡窗口是否有意义?定义一个将来的时间(支持日期结束),之后将重置使用旧方案存储的每个密码,并启用登录逻辑,在该逻辑中,如果用户正确登录,则密码将与新的哈希一起存储(即这样,您可以确保已通过身份验证的用户正在使用系统,并且只有在支持日期结束之前完全没有登录的用户才能重置其密码)。支持日期结束时,旧样式的标志可能会消失。

选项3是最好的-如果实施正确。但是,正如这个较早的答案所显示的那样,很容易引入细微但非常严重的漏洞。选项2更容易正确,也不太差。

#1 楼

您的选择1.是一个坏主意:除了您陈述的用户体验/公共关系原因之外,您还为攻击者提供了一个拦截密码重置令牌并破坏服务器上每个帐户的窗口。如果您甚至有一个懒惰的用户也无法登录/更新密码,这也不能解决您的问题。

乍一看,我觉得2.和3.都不错。 #2的安全性不亚于现在的安全性,但是2.意味着您必须永远继续支持当前的弱登录(或执行类似“在X个月后,我们将擦除您的密码并强迫您执行操作)恢复”会破坏您想要的良好的用户透明性,因此,我们可以忽略它。)让我们考虑一下这种情况,您在数据库中拥有永远不会再次登录的用户。对于2和3.,您必须继续在代码库中永远支持当前的哈希算法,以防万一他们确实登录了,但是至少3.具有防止它们(或更确切地说,您)受到保护的优点如果您的数据库曾经被盗,则进行离线暴力攻击。

由于您必须永远保留“旧样式标志”列,因此请帮自己一个忙,使其成为int而不是bool,这样,如果您不得不再次更新密码哈希算法,就可以记录它们所使用的旧样式。


更新:这里提出了一个非常类似的问题,并基于此线程。

评论


上一段+1。我在回答中拒绝了这一点。

– TTT
16-3-21在15:14



最后一段值得悬赏。不是说我能给一个人,而是让我有自己的感受,而不是虚拟的互联网积分。

– Mindwin
16-3-21在19:04

“让我们考虑一下您的数据库中的用户永远不会再次登录的情况。” -尽管在很多情况下,这仍可以通过另一种推荐的做法来解决:该帐户在一定时间后仍会被锁定,因此您可以在该时间之后放弃对旧算法的支持。

– David Spillett
16 Mar 22 '16 at 10:02

您不必无限期保留旧算法-如果用户12个月未登录,则没有任何要求他们重设密码的事情...很可能他们已经忘记了密码

–HorusKol
16 Mar 24 '16 at 12:25

@Mindwin:当然可以给赏金,随便吧。

– SilverlightFox
16 Mar 27 '16 at 11:22

#2 楼

如果您可以执行选项3,我不知道您为什么还要考虑其他选项。到目前为止,这是最佳选择。使用此选项,我的直觉是考虑使用两种不同的盐,一种用于旧算法,另一种用于带有bcrypt的新算法。我正在设想这样的设置:


设置新密码系统,就像今天开始一样。
创建一个新的单独的表,其中包含用于用户名(或ID),哈希算法(名称或ID),盐。
登录时,检查用户是否在单独的表中有记录,如果是,则以旧方式哈希密码,然后哈希结果新方法并与bcrypt哈希值进行比较。如果匹配,请以新方式重新添加盐/散列密码,然后从单独的表中删除记录。

缺点是您必须将密码在内存中保留几毫秒(谁在乎),那么您每次登录都会获得额外的表查询,几乎永远都是这样,直到单独的表为空或旧帐户变得陈旧到足以让您愿意要求他们自己重置密码为止。

评论


“使用两种盐”?数据库中的每个记录应该有一个;盐不应由使用相同散列方案的所有记录共享。

– Ben Voigt
16年3月21日在18:50

@BenVoigt-当然可以。我指的是对旧哈希和新哈希不使用相同的盐(每个用户)。

– TTT
16-3-21在19:08

@TTT:没关系吗?在OP方案中的任何时间点,您只有一个哈希(密码的旧哈希,密码的旧哈希的新哈希或密码的新哈希的新哈希),因此无论如何都不需要多个盐。

– Matthieu M.
16-3-21在19:42



@MatthieuM。 - 可能是。这就是为什么我称其为“胆量感觉”。它基于这样的理论,即使用不同算法进行双重哈希可以减少熵。我想我正在对此进行推断,并且想知道旧算法是否与新算法有某种联系,共享盐是否可以以某种方式提供额外的信息?我们正在做:F2(salt2,F1(salt1,pass))。对于未知函数1,是否仍然通过salt1 = salt2获取更多信息?可能不是,但是我不知道如何确切地证明它,因此我的“直觉”。

– TTT
16-3-21在19:55



#3 楼

请注意,如果您的OLD方案是用salt进行哈希处理的,则除非您单独存储盐,否则将无法使用方案#3。

通常盐会与哈希一起存储,并且您将盐用作哈希函数的输入-如果不使用完全相同的盐,则将获得完全不同的输出。

如果您使用newhash(oldsalt + oldhash,newsalt),那么具有正确的密码,您将无法重新创建oldhash(因为您没有oldsalt),并且您无法生成最终的哈希。同样的事情也适用于具有参数的任何事物(例如bcrypt具有“ cost”参数-加密时需要设置此参数,并将其嵌入到输出中,以便在验证密码时使用)。

ALSO :就像其他人提到的那样,如果您存储的哈希是“旧”或“新”样式,请考虑存储“方案”,例如0是“旧”,而1是bcrypt(请注意,我不使用“ new”-现在是“ new”,永远不会是“ new”!)。一种常见的方法是在散列的开头添加一个标记(可能已经是这种情况了!)。 bcrypt使用以下标准前缀之一:“ $ 2a $”,“ $ 2b $”,“ $ 2x”或“ $ 2y $”。根据“旧”算法的可能输出,您可能需要组成自己的前缀来标记这些内容,或者您​​可以摆脱“不以'$'开头的任何内容”。

最后,由于您显然对旧密码的安全性感到担心(正确的是!),我建议通过向每个人发送带有令牌的说明(不要这样做!)来强迫所有人更改密码。发送链接!您不希望用户单击链接!只需告诉他们登录到通常的位置即可。然后,要求令牌和密码。否则,过去曾经窃取密码的人可以更改密码,并获得有效的“新”密码。

最后:有一个有效期-如果在该日期之前未更改密码,则它们应该无效。该日期应该在电子邮件中,并且不要太远(一个星期?取决于客户响应的时间)。之后,他们将必须执行“密码重置”过程。

#4 楼

我不知道您的密码编码方案是什么,但是如果它还算不错的话,那么新旧格式的密码结构可能会有所不同。

我已经看到过类似的内容在旧的BSD系统中,当系统从传统的密码编码更改为更安全的密码编码时。新密码以旧模式中不存在的字符序列开头,因此,每次使用旧密码登录的用户都使用旧方法验证其明文密码,然后使用以下方法静默重新哈希并存储回密码数据库中:新方法。一个月后,数据库中不再存在旧密码,而最终用户没有注意任何事情。

那将是第二种和第三种方法之间的某个地方。

我知道在实际的生产Web系统上,情况可能会变得更糟,因为用户可能需要等待数周甚至数月才能再次连接。但是(取决于实际活动)可以通过以下事实缓解:一个未连接几个月的用户可能忘记了密码-或者您可以告诉他他已经这样做了……这意味着我会稍等一下在这里待了3到6个月,然后我将所有旧密码重置为禁止值,从而迫使用户在下次连接时通过被忘记的密码屏幕重置密码。

不错的地方是:


对于普通用户是透明的
没有数据库架构更改-只要密码字段可以接受两种样式
偶然的用户将被简单地处理,就像他们几个月后忘记使用密码而没有使用它

缺点是它迫使您同时实施两种身份验证方法和所有样式密码的自动更新。

#5 楼

您没有提及您使用的语言。 Php的问题在于,各种函数在某些情况下应返回true时应返回false,或听起来像是在完成工作但缺少实际处理所有可能有效输入的逻辑的其他函数。

但是,即使您不使用php,这也是执行您在php中谈论的内容的正确方法。高级别的代码可以为您进行编码提供一个起点。

http://php.net/manual/en/function.password-needs-rehash.php

$password = 'rasmuslerdorf';
$hash = 'y$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';

// The cost parameter can change over time as hardware improves
$options = array('cost' => 11);

// Verify stored hash against plain-text password
if (password_verify($password, $hash)) {
    // Check if a newer hashing algorithm is available
    // or the cost has changed
    if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
        // If so, create a new hash, and replace the old one
        $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
    }

    // Log user in
}


我要做的是将您的#2与使用int进行了提及。通过阅读PASSWORD_DEFAULT上的文档,当找到更好的算法时,它可能会更改,并且由于php升级,出于不安全目的,它们需要删除当前算法。

评论


我认为我的问题语言是不可知的,因为实现并不重要。我拒绝接触php,但其他人将来可能会发现它有用吗?

–疯狂的恐龙
16-3-22在15:08

#6 楼

就旧认证系统中的安全漏洞向用户提供建议,并建议他们更改/重置密码以使用新的认证系统。如果在预定的时间段(可能会在某些地方的“小字样”中提示用户)之后,某些用户仍未更改/重置密码,请停用其帐户并发送电子邮件,指导他们如何再次激活它。这样,您的普通用户会感到自己在“要求”更改密码,而不是“告诉”他们更改密码,因此即使他们确实更改了密码,也不太可能对请求做出负面反应(希望他们会,如果您的咨询电子邮件具有足够的说服力),您将不必为永远无法更改密码的少数用户永远支持旧的身份验证系统。

评论


我不同意这个建议,主要是因为OP已经指出了关于选项1的缺点,还有Mike O在回答中指出的那些缺点。

– TTT
16-3-21在16:22

附带说明,我也不同意您选择“安全漏洞”一词。我什至都不会将存储在服务器上的纯文本密码称为“安全漏洞”。尽管我将其称为极差的做法。

– TTT
16年3月21日在16:30

@ttt尽管在更新密码机制时纯文本不会成为问题,因为您会知道对加密后的内容进行哈希处理。

– Phil Lello
16年3月21日在21:03

@TTT这就是为什么我不使用“选项1”的原因;我使用的折衷方案在实践中类似于选项1,但对用户的效果却不同。

–米歇尔·约翰逊(Micheal Johnson)
16-3-21在21:24

@TTT攻击者如何知道哪些帐户的密码较弱?同样,从服务器前端侧的攻击者的角度来看,密码同样是安全的,因为它们仍然需要被强行使用。如果攻击者掌握了哈希值,则可能会破坏密码。为此,攻击者需要在服务器上注册一个帐户,从服务器上窃取哈希,然后知道哪些密码使用哪些哈希。

–米歇尔·约翰逊(Micheal Johnson)
16-3-22在7:27