当我继续建立越来越多的网站和网络应用程序时,我经常被要求存储用户的密码,如果/当用户有问题时,它们可以被检索(要么通过电子邮件发送一个忘记密码的链接,通过电话等),当我可以与这种做法作斗争时,我做了大量的“额外”编程,使密码重置和管理协助成为可能,而不存储他们的实际密码。

当我无法对抗它(或无法获胜)时,我总是以某种方式对密码进行编码,这样至少它不会以明文形式存储在数据库中——尽管我知道如果我的数据库被黑客攻击,罪犯不需要花费太多时间就能破解密码,所以这让我感到不舒服。

在一个完美的世界里,人们会经常更新密码,而不会在许多不同的网站上重复密码——不幸的是,我知道很多人都有相同的工作/家庭/电子邮件/银行密码,甚至在他们需要帮助的时候免费给我。如果我的数据库安全程序因为某种原因失败了,我不想成为他们财务崩溃的罪魁祸首。

从道德和伦理上讲,我觉得我有责任保护一些用户的生活,即使他们对他们的生活不那么尊重。 我确信有许多方法和论点可以用于盐散列和不同的编码选项,但当您必须存储它们时,是否存在单一的“最佳实践”?在几乎所有的情况下,我使用PHP和MySQL,如果这使任何不同的方式,我应该处理的细节。

Bounty的其他信息

我想澄清的是,我知道这不是你想要做的事情,在大多数情况下,拒绝这样做是最好的。然而,我并不是在寻求关于采用这种方法的优点的说教,我是在寻找如果你确实采用这种方法,应该采取的最佳步骤。

在下面的注释中,我指出了主要面向老年人、智障人士或非常年轻的人的网站,当他们被要求执行安全密码恢复程序时,他们会感到困惑。尽管在这些情况下,我们可能会发现这很简单,但有些用户需要额外的帮助,要么让服务技术人员帮助他们进入系统,要么通过电子邮件/直接显示给他们。

在这样的系统中,如果用户没有得到这种级别的访问帮助,来自这些人口统计数据的流失率可能会阻碍应用程序,所以请在回答时考虑到这样的设置。

谢谢大家

这是一个有趣的问题,有很多争论,我很喜欢。最后,我选择了一个答案,它既保留了密码安全性(我不必保留纯文本或可恢复的密码),又使我指定的用户群能够登录到一个系统,而不存在我在正常密码恢复中发现的主要缺陷。

像往常一样,由于不同的原因,大约有5个答案我想标记为正确,但我必须选择最好的一个——其余的都是+1。谢谢大家!

同时,感谢Stack社区中为这个问题投票和/或将其标记为最喜欢的人。我把获得100票作为一种赞美,希望这个讨论能帮助到和我有同样担忧的人。


当前回答

您可以使用公钥加密密码+盐。对于登录,只需检查存储的值是否等于从用户输入+ salt计算的值。如果需要以明文形式恢复密码,则可以使用私钥手动或半自动地解密。私钥可以存储在其他地方,还可以对称加密(这将需要人工交互来解密密码)。

我认为这实际上有点类似于Windows恢复代理的工作方式。

密码被加密存储 人们无需解密为明文即可登录 密码可以恢复为明文,但必须使用私钥,私钥可以存储在系统之外(如果您愿意,可以存储在银行保险箱中)。

其他回答

不要放弃。你可以用来说服你的客户的武器是不可否认性。如果你可以通过任何机制重建用户密码,你就给了他们的客户一个合法的不可抵赖机制,他们可以拒绝任何依赖于该密码的交易,因为供应商无法证明他们没有重建密码并自己完成交易。如果密码被正确地存储为摘要而不是密文,这是不可能的,因此最终客户要么自己执行交易,要么违反了他对密码的注意义务。无论哪种情况,责任完全落在他身上。我处理过的案件中,金额高达数亿美元。不是你想出错的事。

我也有同样的问题。同样的,我总是认为有人入侵我的系统不是“如果”的问题,而是“什么时候”的问题。

所以,当我必须做一个网站,需要存储一个可恢复的机密信息,如信用卡或密码,我所做的是:

使用openssl_encrypt(字符串$data,字符串$method,字符串$password)进行加密 PHP手册。 数据参数: 敏感资料(例如用户密码) 如果需要,序列化,例如,如果信息是一个数据数组,如多个敏感信息 密码arg:使用一个只有用户知道的信息,比如: 用户牌照 社会保险号码 用户电话 用户母名 在注册时通过电子邮件和/或短信发送的随机字符串 方法参数: 选择一种密码方法,如“aes-256-cbc” 永远不要将password参数中使用的信息存储在数据库(或系统中的任何地方)

当需要检索此数据时,只需使用“openssl_decrypt()”函数并向用户询问答案。例如:“要收到你的密码,请回答这个问题:你的手机号码是多少?”

PS 1:永远不要将存储在数据库中的数据用作密码。如果您需要存储用户的手机号码,那么永远不要使用此信息来编码数据。总是使用只有用户知道的信息,或者非亲属很难知道的信息。

PS 2:信用卡信息,比如“一键购买”,我所做的就是使用登录密码。这个密码在数据库中哈希(sha1, md5等),但在登录时,我将明文密码存储在会话或非持久性(即在内存中)安全cookie中。这个简单的密码永远不会留在数据库中,实际上它总是留在内存中,在节结束时销毁。当用户点击“一键购买”按钮时,系统使用此密码。如果用户使用facebook, twitter等服务登录,那么我会在购买时再次提示密码(好吧,这不是完全的“点击”),然后使用用户曾经登录的服务的一些数据(如facebook id)。

Michael Brooks一直对cwe257直言不讳——事实上,无论您使用什么方法,您(管理员)仍然可以恢复密码。那么下面这些选项怎么样:

使用其他人的公钥(一些外部授权机构)加密密码。这样,您就不能亲自重建密码,用户必须去外部权威机构要求恢复他们的密码。 使用从第二个口令短语生成的密钥加密密码。在客户端进行加密,绝不将其明文传输到服务器。然后,为了恢复,通过从他们的输入重新生成密钥,再次在客户端解密。不可否认,这种方法基本上是使用第二个密码,但您总是可以告诉他们把它写下来,或者使用旧的安全问题方法。

我认为1。是更好的选择,因为它允许您在客户的公司内指定某人持有私钥。确保他们自己生成密钥,并将其与指示一起存储在保险箱等。您甚至可以通过选择只加密密码并向内部第三方提供密码中的某些字符来增加安全性,这样他们就必须破解密码才能猜测它。将这些字符提供给用户,他们可能会记住它是什么!

我认为你真正应该问自己的问题是:“我怎样才能更好地说服别人?”

允许用户检索其原始密码的唯一方法是使用用户自己的公钥对其加密。只有该用户才能解密他们的密码。

步骤如下:

用户在您的站点上注册(当然是通过SSL)而无需设置密码。自动登录或提供临时密码。 您提供存储他们的公共PGP密钥,以便将来检索密码。 他们上传他们的公共PGP密钥。 你让他们设置一个新密码。 他们提交密码。 您使用可用的最佳密码哈希算法(例如bcrypt)哈希密码。在验证下一次登录时使用此选项。 使用公钥加密密码,并单独存储。

如果用户随后询问他们的密码,您将使用加密的(而不是散列的)密码进行响应。如果用户不希望将来能够检索他们的密码(他们只能将其重置为服务生成的密码),可以跳过步骤3和7。