在数据库中存储密码的最佳方法


474

我正在一个必须进行身份验证(用户名和密码)的项目中

它还连接到数据库,所以我想我会将用户名和密码存储在那里。但是,在数据库上的表中仅具有文本字段的密码似乎不是一个好主意。

我正在使用C#并连接到2008 Express服务器。谁能(以尽可能多的示例)建议存储这种类型的数据的最佳方法是什么?

附言:如果可以提供充分的理由,我不希望此信息不存储在数据库中


1
无论做什么,如果要进行加密,请不要像前面提到的那样将密钥存储在代码中。那只是不好的做法。
伍迪

12
“如何正确设置密码?” 是一个至关重要的问题。这是一个棘手的问题,错误会带来严重后果(回想一下Tesco和LinkedIn的情况)。我认为应该在programmers.stackexchange.com
Panic Panic

2
这是更好地坚持标准-看en.wikipedia.org/wiki/PBKDF2你只需要找到你的语言的实现
鲍里斯Treukhov

5
:这个问题回答了广泛的安全论坛security.stackexchange.com/questions/211/...
gioele

Answers:


405

您将密码存储在纯文本字段中是一个可怕的主意,这是正确的。但是,就位置而言,在大多数情况下(您真的会想到(我真的想不出任何反例)),在数据库中存储密码表示是正确的做法。通过表示,我的意思是您想使用盐(每个用户应该有所不同)和安全的1向算法对密码进行哈希处理,然后存储密码,从而丢弃原始密码。然后,当您要验证密码时,可以对值进行哈希处理(使用相同的哈希算法和盐),并将其与数据库中的哈希值进行比较。

因此,尽管您正在考虑这是一件好事,而且这是一个好问题,但这实际上是(至少)这些问题的重复:

为了进一步说明问题,简单地散列密码并存储的危险在于,如果侵入者拥有您的数据库,他们仍然可以使用所谓的Rainbow表来“解密” 密码。密码(至少显示在彩虹表中的密码)。为了解决这个问题,开发人员在密码中添加了,如果正确完成,则使得彩虹攻击根本不可行。请注意,常见的误解是在所有密码中简单地添加相同的唯一且长字符串。虽然这并不可怕,但最好为每个密码添加唯一的盐。阅读更多。


39
我的意思是将密码存储在数据库中,而不是将密码存储在其他位置。将该句子放在上下文之外会使我似乎支持存储普通密码,如果您阅读其余内容,我显然不支持。
Paolo Bergantino

13
不仅是我所说的,我还带他去了很多讨论盐的文章,等等……
Paolo Bergantino 2009年

1
@Paolo Bergantino:您确定帖子中没有错字吗?它说:“在大多数情况下,您都会碰到(并且坦白地说,我想不出任何反例)将密码存储在数据库中是正确的做法。” ??? 似乎与您的意见相矛盾
Mitch Wheat

3
保罗的话直接矛盾了。密码的哈希哈希值不是密码。在数据库中存储密码的盐化哈希值不会在数据库中存储密码。答案的主体非常合适,但是其第一句话极具误导性。
罗伯特·罗斯尼

39
@Robert:危险地接近一个小语义游戏,但是我还是要解决它……
Paolo Bergantino,2009年

54

背景 您从不需要,真的,不需要知道用户的密码。您只想验证传入用户是否知道帐户密码。

散列: 通过强大的散列功能存储散列的用户密码(单向加密)。搜索“ c#加密密码”提供了许多示例。

请参阅在线SHA1散列创建器,以了解散列函数产生什么的想法(但不要将SHA1用作散列函数,而应使用SHA256等更强的函数)。

现在,哈希密码意味着您(和数据库小偷)不应该能够将该哈希值还原回原始密码。

如何使用它: 但是,您说,如何使用存储在数据库中的此混搭密码?

当用户登录时,他们会交给您用户名和密码(以其原始文本形式)。您只需使用相同的哈希码对输入的密码进行哈希处理即可获取存储的版本。

因此,比较两个哈希密码(用户名的数据库哈希以及键入的密码和哈希密码)。您可以通过比较哈希值来判断“他们输入的内容”是否与“原始用户输入的密码”相匹配。

额外信用:

问题:如果我有您的数据库,那么我能否像开膛手约翰一样吃饼干,然后开始创建哈希,直到找到与您存储的哈希密码匹配的密码?(因为用户还是会选择简短的字典单词,所以应该很容易)

答:是的,可以。

因此,您应该“盐分”您的密码。参见有关盐维基百科文章

请参见“如何用salt哈希数据” C#示例


14
不错的帖子,除了一件事:md5和sha1都坏了。您可能应该使用更强大的算法,例如SHA2系列。
Paolo Bergantino

3
谢谢保罗-你是正确的。由于SHA2的使用与MD5和SHA1一样容易,因此请使用更强大的哈希算法。
乔伊

5
SHA-1尚未损坏。但是到了布鲁斯·施耐尔(Bruce Schneier)的阶段:步行,不要跑去SHA-2。
伊恩·博伊德

2
“所以,您应该'给密码加盐'...”但是盐通常与密码一起存储在数据库中,这有什么帮助?攻击者只需将盐添加到要测试的字典攻击短语中即可。除了不会显示重复的密码之外,还有什么安全性呢?
trusktr

4
@joej“您永远不会……真的……需要知道用户的密码”-这是一个非常短视的假设。在许多类型的应用程序中,确实有必要以一种可以检索的方式存储密码。例如,需要经常使用用户提供和更新的存储凭据登录到另一个系统的应用程序。
弗朗西斯科·扎拉波佐(Frank Francisco Zarabozo)2015年


27

最佳安全实践是根本不存储密码(甚至不加密),而是存储加密密码的盐化哈希(每个密码具有唯一的盐)。

这样,(实际上)不可能检索明文密码。


11
韦恩(Wayne),在计算哈希值之前先加盐,只要盐有足够的大小,彩虹表就可以有效地被击败。
Mike Rosenblum

11
@韦恩·哈特曼:不是。如果暴露了盐值,则必须为该特定盐值生成一个新的Rainbow表。彩虹表的要点是事先计算哈希值。没有人会为他的特定盐准备一张彩虹桌子。
伊恩·博伊德


6

正如您提到需要用户名和密码一样,我可能有点偏离主题,我对这个问题的理解当然不是最好的,但是OpenID是否值得考虑?

如果您使用OpenID,那么如果我正确理解该技术并且用户可以使用他们已经拥有的凭据,那么最终根本就不会存储任何凭据,从而无需创建特定于您的应用程序的新身份。

但是,如果所涉及的应用程序仅用于内部使用,则可能不适合

RPX提供了一种很好的简便方法,可以将OpenID支持集成到应用程序中。


我同意openID会让人们感到困惑,但是对于此应用程序,它是公司的内部数据库,我怀疑他们是否希望任何老年人都可以登录。另外,此应用程序无法正常运行也不需要Web访问,因此我不愿意要求它。
Crash893

3

在您的方案中,您可以查看asp.net成员身份,这是一个好习惯,将用户的密码作为哈希字符串存储在数据库中。您可以通过将散列的传入密码与数据库中存储的密码进行比较来对用户进行身份验证。

一切都为此目的而构建,请查看asp.net成员资格


1

如果您不需要反转哈希,我将使用MD5 / SHA1作为密码。当用户登录时,您可以只加密给定的密码并将其与哈希进行比较。在这种情况下,几乎不可能发生哈希冲突,除非有人获得对数据库的访问权并看到他们已经有冲突的哈希。


2
我不会使用MD5进行哈希处理-基本上已损坏 mscs.dal.ca/~selinger/md5collision
zebrabox

3
其实,还不算坏。他们可以做的是为两个不同的文件找到相同的哈希值。他们不能做的就是撤消MD5并获得有效的密码。
waiwai933

2
好吧,那也不会被打破吗?您只需要输入生成相同哈希值的其他密码即可,您无需知道原始密码。解决此问题的方法是在哈希之前先对密码加盐。
mjuarez 2013年

2
@mjuarez,如果您在密码中添加了盐,因为使用MD5,则冲突无所谓,因为您无法使用其他密码
WiiMaxx
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.