关于sql server:在数据库中存储密码的首选方法

Preferred Method of Storing Passwords In Database

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


我在数据库中存储了相当于密码的salted散列值,而不是密码本身,然后总是将散列值与生成的用户传入的散列值进行比较。

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


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


我做的和你描述的一样,只是它存储为一个字符串。I base64编码加密的二进制值。要分配的空间量取决于加密算法/密码强度。

我认为你做得对(因为你用盐)。


  • 存储salt密码的哈希,例如bcrypt(nounce+pwd)。您可能更喜欢bcrypt而不是sha1或md5,因为它可以调整为CPU密集型,因此使暴力攻击的时间更长。
  • 在几次登录错误后向登录表单添加验证码(以避免暴力攻击)
  • 如果您的应用程序有一个"忘记我的密码"链接,请确保它不会通过电子邮件发送新密码,而是发送一个链接到(安全的)页面,允许用户定义新密码(可能只有在确认了一些个人信息,如用户的出生日期之后)。此外,如果应用程序允许用户定义新密码,请确保您要求用户确认当前密码。
  • 显然,要保护登录表单(通常使用https)和服务器本身的安全
  • 通过这些措施,您的用户密码将得到很好的保护,以防:

  • =>离线字典攻击
  • =>实时字典攻击
  • =>拒绝服务攻击
  • =>各种攻击!

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

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

    所需字段的大小由所使用的哈希函数确定。MD5总是输出16个字节,SHA-1总是输出20个字节。一旦您选择了一个哈希函数,您通常会被它卡住,因为更改需要重置所有现有密码。所以,使用一个可变大小的字段并不能为您买任何东西。

    关于执行散列操作的"最佳"方法,我尝试提供关于该主题的其他SO问题的许多答案:

    • 加密密码
    • 加密密码
    • 在.NET中加密密码
    • 盐:秘密还是公开?
    • 哈希迭代


    我使用用户名的sha散列、web配置中的guid和存储为varchar(40)的密码。如果他们想要蛮力/字典,他们也需要黑客的网络服务器的guid。如果找到了密码,用户名就会中断在整个数据库中创建彩虹表。如果用户想更改用户名,我只需同时重置密码。

    1
    2
    3
    4
    5
    System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(
        username.ToLower().Trim(),
        ConfigurationManager.AppSettings("salt"),
        password
    );


    您可以在数据库中使用多个散列,这只需要做一些额外的工作。不过,如果您认为将来最有可能需要支持其他格式的话,这是值得的。我经常使用密码输入,比如

    hashid$salt$hashed password

    其中"hashid"只是我内部用来识别的数字,例如,我使用的是具有特定哈希模式的sha1;"salt"是base64编码的随机salt;"hashed password"是base64编码的哈希。如果需要迁移散列,可以截获使用旧密码格式的人,并在他们下次登录时更改密码。

    正如其他人提到的,您希望小心散列,因为它很容易做一些不真正安全的事情,例如,H(salt,password)比H(password,salt)弱得多,但同时您希望平衡为此所做的努力与网站内容的价值。我经常使用h(密码,salt),密码。

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

    编辑后添加了另一条评论:如果我故意建议使用一种算法,这种算法甚至会将每一个密码的1/10秒散列一次,我就很幸运地被老板嘲笑了。(不是很幸运吗?在我下一次的年度回顾中,他会记下一些事情来讨论。)当你有几十个甚至数百个用户时,燃烧时间并不是问题。如果你推送10万用户,你通常会有多个人同时登录。你需要的是快而有力的东西,而不是慢而有力的东西。"但是信用卡信息呢?"充其量是不诚实的,因为存储的信用卡信息不应该在常规数据库附近的任何地方,而且无论如何都会被应用程序加密,而不是单个用户。


    密码的简单散列,甚至(salt+password)通常都不够。

    见:

    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算法。免费的实现可以在网上找到最流行的语言。


    由于您的问题是关于存储方法和大小,我将解决这个问题。

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

    大小取决于哈希算法。MD5的输出总是16个字节,sha1总是20个字节。sha-256和sha-512分别为32和64字节。如果您使用的是文本编码,那么根据编码方法,您将需要稍微多一些存储空间。我倾向于使用base64,因为存储相对便宜。base64需要大约33%的大字段。

    如果你有每个用户的盐渍,你将需要空间散列也。把它放在一起64位salt+sha1哈希(160位)base64编码需要40个字符,所以我将它存储为char(40)。

    最后,如果您想正确地执行它,您不应该使用单个哈希,而是使用一个键派生函数(如rbkdf2)。sha1和md5散列速度非常快。即使是一个单线程应用程序,每秒也可以散列大约30K到50K个密码,在四核机器上,每秒最多可以散列20万个密码。GPU每秒可以散列100x到1000x的密码,以这种蛮力攻击的速度成为一种可以接受的入侵方法。rbkdf2允许您指定迭代次数来微调散列的"慢"程度。关键是"让系统陷入困境,但要选择一些迭代,以便限制哈希吞吐量的上限(比如每秒500个哈希)。未来的验证方法将包括密码字段中的迭代次数(迭代次数+salt+散列)。这将允许未来不断增加的迭代以跟上更强大的处理器的步伐。为了更加灵活,将来可以使用varchar来允许可能更大的/可选的哈希。

    .NET实现是rfc2892derivebyteshttp://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898派生字节.aspx


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

    它支持多种存储选项,包括:单向哈希、双向加密、MD5+salt。有关详细信息,请访问http://www.asp.net/learn/security。

    如果你不需要太花哨的东西,这对网站是很好的。

    如果您不使用ASP.NET,这里有一个很好的链接,指向来自4Guys和CodeProject的几篇文章

    http://aspnet.4guysfromrolla.com/articles/081705-1.aspxhttp://aspnet.4guysfromrolla.com/articles/103002-1.aspxhttp://www.codeproject.com/kb/security/simpleencryption.aspx