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


76

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


1
我感觉这可能是一个骗局,但我认为可能是一个骗局,事实证明这与实际的数据库登录有关,而不仅仅是存储应用程序/站点登录。
TheTXI

1
您能否澄清您的问题,以说明您使用的是“加密”功能还是“哈希”功能?我认为您是指后者,但区别非常明显。
国家统计局

Answers:


82

我将密码的加盐哈希值存储在数据库中,而不是密码本身,然后始终将哈希值与用户传入的值之一进行比较。

将文字密码数据存储在任何地方都太危险了。这样就无法恢复,但是当有人忘记或丢失密码时,您可以进行一些检查并创建新密码。


@Quinton:那几乎就是我要做的。使用.net加密库,它将对密码进行哈希处理,然后将其传递给数据库并以二进制形式存储。我只能通过散列用户输入并将其与存储的内容进行比较来比较密码。
TheTXI

1
如果您正在使用Java,我建议使用jbcrypt。使用极其简单
Jens Schauder

13
我们也存储咸密码。在我们的例子中,我们将160位的has转换为base64,并将其存储在varchar中-这样做只会使事情变得更容易(字符串更易于读取/写入/查看)。除了全局盐之外,我们还使用用户的ID(sid或guid)预加盐,以便具有相同密码的两个用户不会给出相同的哈希值。这使得字典攻击更加困难(更多的CPU开销),并且当每个人都选择相同的密码时,阻止您同时发现多个密码。
伊恩·博伊德

@IanBoyd,如果您尚未更改salt方法,那么您做错了。它应该是随机的,具有足够的熵和特定于用户的情况,在这种情况下,您只能遵循后者。
Francisco Presencia

@FranciscoPresencia从那以后我切换到了bcrypt。规范的bcrypt实现会为每个哈希生成随机盐。规范的实现还将22字节的哈希值转换为base64,适用于轻松存储在varchar数据库列中。考虑他们的部分的最后一点是定义其哈希字符串的格式,其中盐包含在字符串中。
伊恩·博伊德


15

除了将其存储为String之外,我还执行了与您描述的相同的操作。我Base64编码加密的二进制值。要分配的空间量取决于加密算法/密码强度。

我认为您做对了(假设您使用Salt)。


9
  1. 存储加盐密码的哈希,例如bcrypt(nounce + pwd)。您可能更喜欢bcrypt而不是SHA1或MD5,因为它可以被调整为占用大量CPU,从而使蛮力攻击的方式更长。
  2. 在几次登录错误后将验证码添加到登录表单中(以避免蛮力攻击)
  3. 如果您的应用程序具有“忘记密码”链接,请确保它不通过电子邮件发送新密码,而应将其发送到(安全)页面的链接,以允许用户定义新密码(可能仅在确认后)一些个人信息,例如用户的出生日期)。另外,如果您的应用程序允许用户定义新密码,请确保您要求用户确认当前密码。
  4. 显然,保护登录表单(通常使用HTTPS)和服务器本身的安全

通过这些措施,您的用户密码将受到很好的保护,防止:

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

所有这些措施都可以快速实施,并且花费很少的精力!+1 =)
路加·安顿斯

DOS攻击与密码安全性相关吗?
Arj 2012年

1
@ a12jun:如果您的网站上有一个“忘记密码”链接,其中带有简单的“登录”输入,并且有一个“请生成新密码并通过电子邮件发送给我”按钮,那么知道您的登录名的任何人都可以强制您更改密码。如果他自动执行此操作以每10秒运行一次,那么他可以拒绝您访问您的帐户。如果他针对成千上万的用户运行此服务,那么他可能会吸引许多人不喜欢您的服务。准则3可以保护您免受此侵害,这就是我的意思(我必须承认这不是很清楚)。安全性=机密性,完整性,可用性
MiniQuark 2012年

8

由于哈希函数的结果是一系列介于0到255之间(或-128到127,取决于8位数据类型的有符号性)的字节,因此将其存储为原始二进制字段是最有意义的,因为它是最紧凑的表示形式,并且不需要其他编码和解码步骤。

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

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


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


如果您更仔细地阅读我的问题,您会发现我在数据类型和为该字段分配的空间方面更加专注于实际存储。
TheTXI

但是+1是为了向以后可能会遇到的其他人提供额外的信息。
TheTXI

对了 看来我并不是唯一一个失踪的人;)
Erickson

4

我使用用户名的影子哈希,Web配置中的guid和密码(存​​储为varchar(40))。如果他们想暴力破解/词典,他们也需要破解Web服务器以获得GUID。如果用户名确实找到了密码,则无法在整个数据库中创建彩虹表。如果用户想更改其用户名,我只是在同一时间重设密码。

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

我曾经使用用户名作为添加盐。但是后来我们无法重命名用户。我们改用了Salt作为用户的ID,GUID,SID或其他替代键。
伊恩·博伊德

特鲁,好电话。这样,您不必在重命名用户时重设密码
Shawn

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算法。可以在线找到大多数流行语言的免费实现。


伟大的联系超出了表面的安全考虑。对于故意慢的算法,在客户端计算哈希值以免使服务器(可能已经很繁忙)过载是很重要的吗?
cheduardo

并不是的。目标只是使它“稍微”变慢。只是给你一个想法。我可以在四核系统上每秒SHA1哈希大约200K密码。我怀疑有人每秒需要处理200K个同时登录。...因此,您使用PBKDF2或bcrypt之类的功能将其速度降低到每秒仅100个散列(将暴力时间轴增加200倍)。
杰拉尔德·戴维斯

3

您可以在数据库中使用多个哈希,这只需要一点点额外的努力。这是非常值得的,但是如果您认为将来极少有机会需要支持其他格式。我会经常使用密码输入

{hashId} $ {salt} $ {已隐藏密码}

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

正如其他人提到的那样,您要谨慎对待哈希,因为它很容易做一些并不真正安全的事情,例如,H(salt,password)比H(password,salt)弱得多,但是同时您想要平衡为此付出的努力与网站内容的价值。我经常会使用H(H(password,salt),password)。

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

编辑后添加了另一条评论:如果我有意建议使用一种算法,该算法甚至烧掉每个密码的1/10秒散列值,我很幸运被老板办公室笑了。(不是很幸运吗?在我的下一次年度审查中,他会记下一些要讨论的内容。)如果您有数十个甚至数百个用户,那么花费时间并不是问题。如果您要推动10万个用户,通常会有多个人同时登录。您需要快速而强大的东西,而不是缓慢而强大的东西。“但是信用卡信息呢?” 充其量是不屑一顾的,因为存储的信用卡信息不应位于常规数据库附近,并且无论如何都将由应用程序而非个人用户进行加密。


在散列方面,没有快速而强大的功能。抱歉。如果对您来说速度很快,那么对暴力攻击者来说速度很快。使用密钥派生功能是加强密码的有效方法。
杰拉尔德·戴维斯

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


2

由于您的问题是关于存储方法和大小的,所以我会解决。

存储类型可以是二进制或文本表示形式(最常见的是base64)。Binary较小,但我发现使用文本更容易。如果要按用户加盐(每个密码加盐),则将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密码,而在四核计算机上,则高达每秒200K密码。GPU每秒可以散列100倍至1000倍的密码,而像暴力破解这样的速度成为一种可以接受的入侵方法。RBKDF2允许您指定迭代次数,以微调散列的“速度”。关键是使系统瘫痪,但要选择许多迭代,以便限制哈希吞吐量的上限(例如每秒500个哈希)。一种未来的证明方法是在密码字段中包含迭代次数(迭代次数+盐+哈希值)。这将允许将来增加迭代次数,以与功能更强大的处理器保持同步。为了更加灵活,请使用varchar,以允许将来可能使用更大/替代的哈希值。

.Net实现为RFC2892DeriveBytes http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rfc2898derivebytes.aspx

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.