【问题标题】:Preferred Method of Storing Passwords In Database在数据库中存储密码的首选方法
【发布时间】:2023-03-09 09:52:01
【问题描述】:

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

【问题讨论】:

  • 我感觉这可能是个骗局,但我认为可能是个骗局,结果证明与实际数据库登录有关,而不仅仅是存储应用程序/站点登录。
  • 能否请您澄清您的问题以解释您使用的是“加密”还是“散列”功能?我认为您的意思是后者,但差异非常显着。

标签: sql-server database security encryption passwords


【解决方案1】:

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

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

【讨论】:

  • @Quinton:这就是我所做的。使用 .net 加密库,它将对密码进行哈希处理,然后我将其传递到数据库并以二进制文件的形式存储。我只能通过散列用户输入并将其与存储的内容进行比较来比较密码。
  • 如果您使用 java,我推荐使用 jbcrypt。使用极其简单
  • 我们也存储加盐密码。在我们的例子中,我们将 160 位必须转换为 base64,并将其存储在 varchar 中 - 它只是让事情变得更容易(字符串更容易读/写/查看)。除了全局加盐之外,我们还使用用户的 id(sid 或 guid)预加盐,这样两个具有相同密码的用户就不会给出相同的哈希值。这使得字典攻击更加困难(更多的 CPU 成本),并防止您在每个人都选择相同的密码时同时发现多个密码。
  • @IanBoyd ,如果您没有更改盐方法,那么您做错了。它应该是随机的,具有足够的熵和用户特定的,在这种情况下你只遵守后者。
  • @FranciscoPresencia 从那时起我就改用 bcrypt。规范的 bcrypt 实现为每个哈希生成随机盐。规范实现还将 22 字节哈希转换为 base64,适合轻松存储在 varchar 数据库列中。他们的最后一点好的想法是定义他们的哈希字符串的格式,其中盐包含在字符串中。
【解决方案2】:

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

【讨论】:

  • 修改,不得不这样做,只是为了开玩笑;)
【解决方案3】:

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

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

【讨论】:

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

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

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

    【讨论】:

    • 所有这些措施都可以快速实施,并以最小的努力打包! +1 =)
    • DOS攻击与密码安全有关吗?
    • @a12jun:如果您的网站上有“忘记密码”链接,只需简单的“登录”输入,以及“请生成新密码并通过电子邮件发送给我”按钮,那么任何知道您登录信息的人都可以强制您更改密码。如果他每 10 秒自动运行一次,那么他可以拒绝您访问您的帐户。如果他对成千上万的用户运行这个,他可能会让很多人不喜欢你的服务。准则 #3 可以保护您免受这种情况的影响,这就是我的意思(我必须承认,这不是很清楚)。安全性 = 机密性、完整性、可用性
    【解决方案5】:

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

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

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


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

    【讨论】:

    • 如果您更仔细地阅读我的问题,您会发现我在数据类型和为字段分配的空间方面更关注实际存储。
    • 但是 +1 可以为以后可能出现的其他人提供额外信息。
    • 正确。看来我不是唯一一个错过的人;)
    【解决方案6】:

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

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

    【讨论】:

    • 我曾经使用用户名作为添加盐。但后来我们无法重命名用户。我们改用 salt 和用户的 id、guid、sid 或其他代理键。
    • 真的,好电话。这样您在重命名用户时不必重置密码
    【解决方案7】:

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

    见:

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

    【讨论】:

    • 超越表面安全问题的优秀链接。对于故意慢速的算法,在客户端计算哈希值以免使(可能已经很忙)服务器超载很重要吗?
    • 并非如此。目标只是让它“稍微”慢一点。只是给你一个想法。我可以在四核系统上每秒对大约 20 万个密码进行 SHA1 哈希处理。我怀疑任何人都需要每秒处理 200K 的同时登录......所以你使用像 PBKDF2 或 bcrypt 这样的函数来减慢它,说每秒只有 100 个哈希(将蛮力时间线增加 200 倍)。跨度>
    【解决方案8】:

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

    这样的密码条目

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

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

    正如其他人提到的那样,您要小心使用哈希值,因为很容易做一些不太安全的事情,例如,H(salt,password) 远比 H(password,salt) 弱,但同时您希望在为此付出的努力与网站内容的价值之间取得平衡。我会经常使用 H(H(password,salt),password)。

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

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

    【讨论】:

    • 在散列方面没有快速和强大的东西。对不起。如果它对您来说很快,那么对于蛮力攻击者来说也很快。使用密钥派生函数是一种有效的密码强化方法。
    【解决方案9】:

    如果您使用 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

    【讨论】:

      【解决方案10】:

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

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

      大小取决于哈希算法。 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/en-us/library/system.security.cryptography.rfc2898derivebytes.aspx

      【讨论】:

        猜你喜欢
        • 2012-02-16
        • 1970-01-01
        • 2020-08-10
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多