在数据库中存储密码的首选方法

时间:2009-03-05 17:10:36

标签: sql-server database security encryption passwords

在数据库中存储密码的首选方法/数据类型是什么(最好是SQL Server 2005)。我在几个应用程序中一直使用的方法是首先使用.NET加密库,然后将它们作为二进制文件存储在数据库中(16)。这是首选方法还是我应该使用不同的数据类型或分配比16更多的空间?

10 个答案:

答案 0 :(得分:81)

我将密码的salted哈希等价物存储在数据库中,而不是密码本身,然后总是将哈希值与用户传入的哈希值进行比较。

将文字密码数据存储在任何地方都太危险了。这使得恢复成为不可能,但是当有人忘记或丢失密码时,您可以通过一些检查并创建新密码。

答案 1 :(得分:48)

首选方法:永远不要在您的数据库中存储密码。只有哈希。加盐调味。

答案 2 :(得分:15)

我做了你所描述的同样的事情,除了它存储为一个字符串。 I Base64编码加密的二进制值。分配的空间量取决于加密算法/密码强度。

我认为你做得对(假设你使用Salt)。

答案 3 :(得分:9)

  1. 存储salted-password的哈希值,例如bcrypt(nounce + pwd)。您可能更喜欢bcrypt而不是SHA1或MD5,因为它可以调整为CPU密集型,因此可以更长时间地进行暴力攻击。
  2. 在几次登录错误后添加验证码(以避免暴力攻击)
  3. 如果您的应用程序有“忘记密码”链接,请确保它不通过电子邮件发送新密码,而是应该发送一个链接到(安全)页面,允许用户定义新密码(可能只有在确认某些个人信息后,例如用户的出生日期。此外,如果您的应用程序允许用户定义新密码,请确保您要求用户确认当前密码。
  4. 显然,保护登录表单(通常使用HTTPS)和服务器本身
  5. 通过这些措施,您的用户密码将得到相当好的保护,以防止:

    1. =>离线字典攻击
    2. =>现场字典攻击
    3. =>拒绝服务攻击
    4. =>各种各样的攻击!

答案 4 :(得分:8)

由于散列函数的结果是0到255范围内的一系列字节(或-128到127,取决于8位数据类型的有符号关系),因此将其存储为原始二进制字段会使最有意义的,因为它是最紧凑的表示,不需要额外的编码和解码步骤。

某些数据库或驱动程序对二进制数据类型没有很好的支持,或者有时开发人员不熟悉它们以使其感觉舒适。在这种情况下,使用二进制文本编码(如Base-64或Base-85),并将结果文本存储在字符字段中是可以接受的。

必要字段的大小由您使用的哈希函数决定。 MD5总是输出16个字节,SHA-1总是输出20个字节。一旦选择了哈希函数,您通常会坚持使用它,因为更改需要重置所有现有密码。因此,使用可变大小的字段并不能为您带来任何好处。


关于执行散列的“最佳”方式,我试图提供关于该主题的其他SO问题的许多答案:

答案 5 :(得分:4)

我使用用户名的sha哈希,web配置中的guid和密码,存储为varchar(40)。如果他们想要暴力/字典,他们也需要破解Web服务器以获取guid。如果用户名确实找到了密码,则会在整个数据库中创建彩虹表。如果用户想要更改用户名,我只需要同时重置密码。

System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(
    username.ToLower().Trim(),
    ConfigurationManager.AppSettings("salt"),
    password
);

答案 6 :(得分:3)

密码的简单哈希,甚至(盐+密码)通常都不够。

请参阅:

http://www.matasano.com/log/958/enough-with-the-rainbow-tables-what-you-need-to-know-about-secure-password-schemes/

http://gom-jabbar.org/articles/2008/12/03/why-you-should-use-bcrypt-to-store-your-passwords

两者都推荐使用bcrypt算法。可以在线找到大多数流行语言的免费实施。

答案 7 :(得分:3)

您可以在数据库中使用多个哈希值,只需要一点额外的努力。如果您认为将来需要支持其他格式的机会最为遥远,那么这是值得的。我经常使用密码条目,如

{hashId} $ {salt} $ {哈希密码}

其中“hashId”只是我在内部用来识别的一些数字,例如,我正在使用具有特定散列模式的SHA1; “盐”是base64编码的随机盐;和“散列密码”是base64编码的散列。如果您需要迁移哈希,您可以使用旧密码格式拦截人员,并在下次登录时让他们更改密码。

正如其他人提到的那样,你要小心你的哈希,因为很容易做一些不太安全的事情,例如,H(盐,密码)远远弱于H(密码,盐),但同时您希望平衡投入到此中的工作量与网站内容的价值。我经常使用H(H(密码,盐),密码)。

最后,与能够使用期望文本数据的各种工具的好处相比,使用base64编码密码的成本是适中的。是的,他们应该更灵活,但是你准备告诉你的老板他不能使用他最喜欢的第三方工具,因为你想节省每个记录几个字节? : - )

编辑添加另一条评论:如果我建议故意使用一种算法甚至烧掉每个密码的十分之一秒,我很幸运会被老板的办公室嘲笑。 (不是很幸运?他会在我的下一次年度评论中记下一些事情来讨论。)当你有几十个甚至几百个用户时,燃烧那个时间不是问题。如果你推动100k用户,你通常会有多个人同时登录。你需要快速而强壮的东西,而不是缓慢而强烈的东西。 “但信用卡信息怎么样?”最好是不诚实,因为存储的信用卡信息不应该在您的常规数据库附近,并且无论如何都会被应用程序加密,而不是单个用户。

答案 8 :(得分:2)

如果您正在使用ASP.Net,则可以使用内置的成员资格API。

它支持多种类型的存储选项,包括;单向散列,双向加密,md5 + salt。 http://www.asp.net/learn/security了解更多信息。

如果您不需要任何太过花哨的东西,这对网站来说非常棒。

如果您没有使用ASP.Net,这是一个很好的链接到4guys和codeproject的一些文章

http://aspnet.4guysfromrolla.com/articles/081705-1.aspx http://aspnet.4guysfromrolla.com/articles/103002-1.aspx http://www.codeproject.com/KB/security/SimpleEncryption.aspx

答案 9 :(得分:2)

因为您的问题是关于存储方法&我将解决这个问题。

存储类型可以是二进制或文本表示(base64是最常见的)。二进制文件较小,但我发现使用文本更容易。如果您正在进行每个用户salting(每个密码使用不同的盐),那么将salt + hash存储为单个组合字符串会更容易。

大小取决于哈希算法。 MD5的输出始终为16字节,SHA1始终为20字节。 SHA-256& SHA-512是32&分别为64字节。如果使用文本编码,则需要稍多的存储空间,具体取决于编码方法。我倾向于使用Base64,因为存储相对便宜。 Base64将需要大约33%的字段。

如果你有每个用户腌制,你也需要空间用于哈希。总而言之,64位盐+ SHA1散列(160位)base64编码需要40个字符,因此我将其存储为char(40)。

最后,如果你想做得对,你不应该使用单个哈希,而是使用像RBKDF2这样的密钥派生函数。 SHA1和MD5哈希非常快。即使是单线程应用程序也可以每秒散列大约30K到50K密码,在四核机器上每秒最多可达200​​K密码。 GPU可以每秒散布100倍到1000倍的密码。像蛮力攻击这样的速度成为可接受的入侵方法。 RBKDF2允许您指定迭代次数,以微调散列的“慢”程度。关键是要使系统陷入困境,而是选择多次迭代,以便限制哈希吞吐量的上限(比如每秒500个哈希值)。未来的证明方法是在密码字段中包含迭代次数(迭代+ salt + hash)。这将允许未来增加迭代以跟上更强大的处理器的步伐。为了更加灵活,使用varchar可以在将来允许更大/替代的哈希值。

.Net实现是RFC2892DeriveBytes http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx