【问题标题】:How does one store password hashes securely in memory, when creating accounts?创建帐户时,如何将密码哈希安全地存储在内存中?
【发布时间】:2009-03-14 16:45:50
【问题描述】:

我们基于 Web 的应用程序将用户帐户绑定到具有在帐户创建期间指定的密码的用户。在 Java 的情况下,如何安全地处理密码,然后将其哈希保存在数据库中。

更具体地说,如何确保保存密码的字符串在足够短的时间间隔内被垃圾回收?

【问题讨论】:

标签: java security hash passwords


【解决方案1】:

如果有可能(在 Web 应用程序中可能很难),最好将密码存储在字符数组中,而不是将它们存储在字符串中。如果您完成了密码的存储,您可以使用Array.fill() 将其覆盖在内存中,并通过丢弃它使引用可供垃圾收集器使用:

Arrays.fill(password, ' ');
password = null;

我刚刚注意到,取消密码有点偏执,但如果它让你放心,你可以这样做:)

【讨论】:

  • 如果这在 Web 应用程序中是可能的 - 从 servlet 输入流中读取密码到字节数组中,然后在保存密码后用零填充数组,那么它应该是一个可行的方法。 OWASP Java 项目在一个例子中使用了这个,但不是用于持久化密码。
  • 这不是要求垃圾收集器不压缩堆栈,从而在内存中的其他地方留下数组的副本吗?
  • @KeeperOfTheSoul:是的,最终它取决于 GC 行为。它还取决于数组是否会被序列化。总而言之,我希望 Java 有一个类似于 Microsoft .Net 中的 SecureString 类。
  • 此解决方案是否通过不允许任何人通过内存转储获取密码来保护密码?
【解决方案2】:

您不使用字符串。您使用 char[],然后在完成后覆盖 char[]。

在垃圾回收方面绝对不能保证(除了终结器将在对象被回收之前运行)。 GC 可能永远不会运行,如果它运行它可能永远不会 GC 包含密码的字符串。

【讨论】:

    【解决方案3】:

    如果你在客户端创建哈希,应该不需要考虑这个问题。明文密码永远不会提交给服务器。

    【讨论】:

    • 是的,这是一个很好的方法,但是这样不是也让攻击者能够研究使用中的哈希算法吗?诚然,它在某种程度上是通过默默无闻来实现安全的,但它确实限制了攻击者可获得的有关密码存储方式的信息量。
    • 这是一个选项,但攻击者不能拦截客户端生成的哈希,然后再次生成相同的 HTTP 请求以登录?
    • @Markus,始终使用 HTTPS,因此 MITM 攻击的概率很低或为零。
    • 在使用 HTTPS 的情况下为什么还要麻烦客户端加密?
    • 这样你只需要重新定义明文密码是什么。所以你显然需要在服务器上再次散列,而你的方案根本没有帮助解决 OP 的问题。
    【解决方案4】:

    两个词:局部作用域。用于密码处理的声明变量需要具有可能的绝对最小范围。

    一旦变量超出范围,对象就有资格进行垃圾回收。

    通常,您是从请求中挑选内容。您需要一个非常非常小的事务来接受请求、散列密码、持久化并重定向。然后,您重定向到的页面可以获取内容并执行属于您的应用程序的所有“其他”处理。

    【讨论】:

      【解决方案5】:

      在 Java 中无法保证从内存中删除明文密码。

      但是,黑客不需要访问程序的内存来获取明文密码。有很多更简单的方法(例如嗅探数据包),因此任何人都不太可能依赖这种方法。

      最好的方法是让客户端按照@Mork0075 的建议加密密码。但是,虽然这意味着您无法轻易获取密码,但程序仍然可以获取密码的加密版本,从而伪装成用户。解决此问题的一种方法是使用 SSL 加密整个连接。

      所有这些都是相当学术性的,因为黑客最简单的方法是监视发送到数据库的数据包并获取数据库的密码。我怀疑直接访问您的数据库更令人担忧……或者可能不是。 ;)

      【讨论】:

      • 我正在考虑保护密码,而不是因为攻击者会尝试这种方法 - 如果他能够窥视内存,那么这个盒子就已经被攻破了,所以还有更大的事情需要担心。我正在设法防止密码出现在为诊断而获取的 Java 堆转储中。
      【解决方案6】:

      使用密码挑战:

      1. 服务器选择一个挑战值并发送给客户端
      2. 服务器使用 密码 和挑战执行单向转换,例如。 MD5(CONCAT(challenge, password)) 并将其分配给会话。
      3. 纯文本密码现在超出范围,可以进行垃圾回收了。
      4. 客户端也执行相同的转换并将结果发送到服务器。
      5. 如果服务器和客户端选择相同的最终值,则客户端通过身份验证。

      此方法可防止重放攻击,但要求挑战值非常不可预测(随机)且不经常重复使用(长时间)。

      纯文本密码仅在处理初始连接请求的范围内 - 而不是在身份验证期间。单向翻译结果在作用域内多久(不是垃圾收集)并不重要,因为它几乎没有重播价值。

      【讨论】:

      • 这在登录过程中可以正常工作,但作者要求注册一个帐户,所以你也必须在数据库中存储一个哈希值。此哈希无法通过挑战值生成,因为它无法在登录时恢复。
      猜你喜欢
      • 2013-12-25
      • 1970-01-01
      • 1970-01-01
      • 2017-07-26
      • 2017-06-05
      • 1970-01-01
      • 2017-12-16
      • 1970-01-01
      • 2015-10-07
      相关资源
      最近更新 更多