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

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

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

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

Bounty的其他信息

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

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

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

谢谢大家

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

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

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


当前回答

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

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

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

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

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

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

其他回答

在独立服务器上打开一个数据库,并给每个需要此功能的web服务器一个加密的远程连接。 它不必是关系DB,它可以是具有FTP访问权限的文件系统,使用文件夹和文件而不是表和行。 如果可以的话,给web服务器只写权限。

像正常人一样,在网站的数据库中存储不可检索的密码加密(让我们称之为“pass-a”):) 对于每个新用户(或密码更改),在远程DB中存储密码的普通副本。使用服务器id,用户id和“pass-a”作为这个密码的组合密钥。你甚至可以在密码上使用双向加密,这样晚上睡得更好。

现在,为了让某人同时获得密码和它的上下文(站点id +用户id +“pass-a”),他必须:

入侵网站的数据库,以获得(“pass-a”,用户id)对或对。 从配置文件中获取网站的id 找到并侵入远程密码数据库。

您可以控制密码检索服务的可访问性(仅将其公开为安全的web服务,每天只允许一定数量的密码检索,手动进行,等等),甚至为此“特殊安全安排”收取额外费用。 密码检索数据库服务器是相当隐藏的,因为它不提供很多功能,可以更好地保护(你可以定制权限,进程和服务紧密)。

总而言之,你让黑客的工作变得更加困难。在任何一台服务器上出现安全漏洞的几率都是一样的,但是有意义的数据(帐户和密码的匹配)将很难组合起来。

用户是否真的需要恢复(例如被告知)他们忘记的密码是什么,或者他们只是需要能够进入系统?如果他们真正想要的是登录密码,为什么不设置一个程序,简单地将旧密码(不管是什么)更改为支持人员可以提供给丢失密码的人的新密码?

我曾经使用过这样的系统。支持人员无法知道当前密码是什么,但可以将其重置为新值。当然,所有这些重置都应该记录在某个地方,最好的做法是生成一封电子邮件给用户,告诉他密码已被重置。

另一种可能是使用两个同时的密码来访问一个帐户。一个是用户管理的“正常”密码,另一个就像一个骨架/主密钥,只有支持人员知道,对所有用户都是一样的。这样,当用户有问题时,技术支持人员可以用万能钥匙登录到帐户,并帮助用户更改密码。不用说,所有使用主密钥的登录都应该由系统记录。作为一种额外的措施,无论何时使用主密钥,您都可以验证支持人员凭据。

- edit -回应关于没有主密钥的评论:我同意这是不好的,就像我认为允许用户以外的任何人访问用户的帐户是不好的一样。如果你看一下这个问题,整个前提是客户要求一个高度妥协的安全环境。

万能钥匙不需要像乍看之下那么糟糕。我曾在一家国防工厂工作,他们认为主机操作员在某些场合需要有“特殊访问权限”。他们只是把特殊的密码放在一个密封的信封里,并把它贴在操作员的桌子上。要使用密码(接线员不知道),他必须打开信封。每次换班时,值班员的一项工作就是查看信封是否被打开,如果打开了,立即(由其他部门)更改密码,新密码放入新信封,然后重新开始这个过程。操作员将被问及他为什么打开它,这一事件将被记录在案。

虽然这不是我设计的程序,但它确实有效,并提供了良好的问责制。每件事都被记录和审查过,加上所有的操作员都有国防部的秘密许可我们从未有过任何滥用。

由于审查和监督,所有操作员都知道,如果他们滥用打开信封的特权,他们将立即被解雇,并可能受到刑事起诉。

所以我想真正的答案是,如果一个人想把事情做好,就雇佣他们可以信任的人,进行背景调查,并行使适当的管理监督和问责制。

但话说回来,如果这个可怜的家伙的客户有良好的管理,他们就不会在第一时间要求这样一个安全折衷的解决方案,不是吗?

另一个你可能没有考虑到的选择是允许通过电子邮件进行操作。这有点麻烦,但我为一个客户端实现了这个功能,该客户端需要系统“外部”的用户来查看(只读)系统的某些部分。例如:

一旦用户注册,他们就拥有完全的访问权限(就像普通用户一样) 网站)。注册必须包括电子邮件。 如果需要数据或操作,而用户不需要 记住他们的密码,他们仍然可以通过 点击一个特殊的“给我发邮件以获得许可”按钮,就在常规的“提交”按钮旁边。 然后,请求以超链接发送到电子邮件,询问他们是否希望执行该操作。这类似于密码重置电子邮件链接,但它不是重置密码,而是执行一次性操作。 然后用户点击“Yes”,它确认应该显示数据,或者应该执行操作,显示数据等等。

正如你在评论中提到的,如果电子邮件被泄露,这将不起作用,但它确实解决了@joachim关于不想重置密码的评论。最终,他们将不得不使用密码重置,但他们可以在更方便的时候这样做,或者根据需要在管理员或朋友的帮助下这样做。

该解决方案的另一种方法是将操作请求发送给第三方可信管理员。这对于老年人、智障人士、非常年轻或其他困惑的用户来说效果最好。当然,这需要一个可信的管理员来支持这些人的行动。

我以实现多因素身份验证系统为生,所以对我来说,很自然地认为可以重置或重构密码,同时暂时少使用一个因素来验证用户,仅用于重置/重新创建工作流。特别是使用otp(一次性密码)作为一些额外的因素,如果建议的工作流的时间窗口很短,则可以降低很大程度的风险。我们为智能手机(大多数用户已经随身携带了一整天)实现了软件OTP生成器,并取得了巨大的成功。在对商业插头的抱怨出现之前,我想说的是,当密码不是用于验证用户身份的唯一因素时,我们可以降低密码易于检索或重置的固有风险。我承认,在网站场景之间的密码重用情况仍然不太好,因为用户会坚持拥有原始密码,因为他/她也想打开其他网站,但你可以尝试以最安全的方式(htpps和谨慎的html外观)提供重建的密码。

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

步骤如下:

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

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