【问题标题】:Why not DriverManager.getConnection(String url, String user, char[] password)?为什么不 DriverManager.getConnection(String url, String user, char[] password)?
【发布时间】:2011-01-17 00:22:27
【问题描述】:

我们知道使用 char[] 而不是 java.lang.String 来存储密码是一种很好的做法。这是出于以下两个原因(如我所读):

  1. char[] 是可变的,因此我们可以在使用后清除密码。
  2. 字符串文字进入一个不会像其他对象一样被垃圾收集的池,因此可能会出现在内存转储中。

但是java.sql.DriverManager没有符合上述最佳实践的getConnection(),因为它的密码参数是String。

DriverManager.getConnection(String url, String user, String password)

我认为 API 应该有一个具有以下签名的重载方法:

DriverManager.getConnection(String url, String user, char[] 密码)

您对此有何看法?你有什么替代方法可以克服这个缺点吗?

很想听听你的想法。

【问题讨论】:

    标签: java security string char


    【解决方案1】:

    String literals 进入池中,但我不希望密码是文字(在程序中硬编码)。我希望它来自配置或类似的。因此,它不会是文字,并且 被垃圾收集(假设所有引用都被分箱)。

    【讨论】:

      【解决方案2】:

      只能推测,除非你可以询问 API 的设计者。

      我的猜测是这样的:

      我知道的使用char[] 而不是String 作为密码的主要API 是JPasswordField。这样做是为了允许程序在使用其他值后用其他值覆盖用户输入的密码(这对于 String 是不可能的,它会留在内存中至少直到它被垃圾收集)。

      在我知道的大多数应用程序中,用于连接数据库的密码通常不是用户输入的,而是来自某种配置/目录/...

      不管它来自哪里:一旦它不再为人所知,应用程序就可以重新构建它。这是用户输入的密码和以编程方式获取的密码之间的最大区别。

      由于通常可以通过比搜索程序已使用的所有内存更简单的方式获取密码,因此修复此攻击向量不是高优先级

      </speculation>

      【讨论】:

        【解决方案3】:

        你的理由 #2 是我见过的最常使用 char[] 而不是 String 的理由。但是,总的来说,我认为如果您假设某人可以通过调试器或其他方式访问您的程序状态,那么他们没有理由不能同时查看 char[] 的内容。

        【讨论】:

          【解决方案4】:

          使用 char[] 数组的参数最适用于用户键入密码的情况,并且您希望最大限度地减少密码的暴露。

          JDBC 连接几乎从不这样。密码存储在某个配置文件中,可能被混淆了,并且比读取内存的可能性更大。

          无论如何,JDBC 标准允许在属性对象甚至 URL 中传递密码,所以我认为使用 char[] 密码参数会给你一种错误的安全感。

          如果您有一个真实的用例,即密码需要尽快从内存中删除以使其不可恢复,否则这在真实 JDBC 应用程序的上下文中是有意义的,我很想听听。我可以想象这样的事情可能存在,但我想不出使用char[] 真的让事情更安全的场景。

          【讨论】:

          • 关于“JDBC 连接几乎从来不是那样”的有趣注释。我是来寻找这个主题的,因为在我们的例子中它的。用户正在输入密码,我们试图使其尽可能安全,然后某些 API(即 JDBC 和 URL)以这样的主要方式搞砸了,然后我们又回到没有通过切换到 char 来改进任何东西[]。 :(
          【解决方案5】:

          您的第 1 点在某种程度上是有效的,但第 2 点关于在未收集的字符串池中结束的字符串仅适用于静态文字字符串(直接出现在代码中的字符串)。如果您将密码直接存储在您的代码中,我建议您首先担心这些,因为在类文件中找到它们比在正在运行的 VM 中要容易得多。

          因此,如果您认为必须将密码存储在 char[] 中,您仍然可以在调用 getConnection() 之前从中创建一个 String 对象。如果驱动程序实现不在内部存储字符串对象,它将很快无法再访问,因此很快就会被收集。那时唯一仍然读取它的机会是直接检查 JVM 的内存,它可能仍然存在。所以你是对的,使用 char[] 会更安全一些,但不会太多,因为无论如何密码都必须在内存中。

          即使用任何类型的 API 获取密码的一种非常简单的方法是注入伪造的 SQL 驱动程序并直接在 connect 方法中获取密码。能够检查 JVM 内存以在其中找到尚未覆盖的密码字符串的人也将能够注入修改后的驱动程序 jar。

          【讨论】:

          • 关于 JDBC 的可插拔特性如何使您可以很容易地将驱动程序替换为您自己的数据库。
          【解决方案6】:

          你的观点很好,大多数回答的人都错过了它。最坏的情况是操作系统换出了“临时”密码,请以明文形式提醒您。在此之后,这个交换文件很容易被读取,如果攻击者拥有大约 1500 美元的设备,磁盘上的文件甚至可以被零覆盖而幸存下来......

          您可以做的最好的事情是连接,密码 = null,然后显式调用 gc()。如果幸运的话,这甚至会清除驱动程序实现使用的字符串。

          随便...

          • 你问的问题目前是不可能的,但会很好。
          • 即使 char[] 也可以在某些条件下(例如交换)存活。
          • 您的密码在进入数据库的过程中会被多次复制,因此 char[] 只能解决一部分问题。

          现实情况是,除非平台是专门为安全设计的,否则所有方法都只是拖延战术。尽你所能,但如果确定的人可以访问你的机器,char[] 最多将为你赢得 1 小时。

          【讨论】:

            猜你喜欢
            • 2012-03-16
            • 2012-06-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-24
            • 1970-01-01
            相关资源
            最近更新 更多