如何确保在 Java 中销毁 String 对象?

2022-09-01 02:41:15

我公司的一位员工需要通过我制作的程序修改SQL Server数据库中的数据。该程序最初使用Windows身份验证,我要求DBA授予此特定用户对所述数据库的写入访问权限。

他们不愿意这样做,而是授予我的Windows用户帐户的写入权限。

由于我信任这个人,但不足以让他在我的会话打开的情况下工作90分钟,所以我只需在我的程序中添加登录提示,要求输入用户名和密码组合,然后使用它登录到SQL Server。我会登录,并相信我的应用程序让他只做他需要做的事情。

但是,这会带来很小的安全风险。SunOracle站点上的密码字段教程指出,密码应保持在内存中所需的最短时间,为此,该方法返回一个数组,一旦你完成它,你可以将其归零。getPasswordchar[]

但是,Java的类只接受对象作为密码,因此一旦我完成密码,我将无法立即处理密码。而且,由于我的应用程序在分配和内存要求方面恰好非常低,谁知道它会在内存中存活多长时间呢?如上所述,该程序将运行相当长的时间。DriverManagerString

当然,我无法控制 SQL Server JDBC 类对我的密码执行的任何操作,但我希望我能控制使用密码执行的操作。

有没有一种万无一失的方法可以用Java销毁/清零对象?我知道两者都有点反对语言(对象破坏是非确定性的,对象是不可变的),也是不可预测的,但仍然;任何想法?StringStringSystem.gc()


答案 1

所以,这是个坏消息。我很惊讶还没有人提到它。对于现代垃圾收集器,甚至整个char[]概念都被打破了。无论您使用字符串还是 char[],数据最终都可能在内存中存在很长时间。为什么?因为现代jvms使用代际垃圾回收器,简而言之,将对象复制到所有地方。因此,即使您使用char[],它使用的实际内存也可能被复制到堆中的不同位置,无论它走到哪里都会留下密码的副本(并且没有高性能gc会将旧内存清零)。因此,当您在最后将实例清零时,您只会将内存中的最新版本清零。

长话短说,没有防弹处理它的方法。你几乎必须信任这个人。


答案 2

我只能想到使用反射的解决方案。可以使用反射来调用使用共享字符数组的私有构造函数:

  char[] chars = {'a', 'b', 'c'};
  Constructor<String> con = String.class.getDeclaredConstructor(int.class, int.class, char[].class);
  con.setAccessible(true);
  String password = con.newInstance(0, chars.length, chars);
  System.out.println(password);

  //erase it
  Arrays.fill(chars, '\0');
  System.out.println(password);

编辑

对于任何认为这是一种万无一失甚至有用的预防措施的人,我鼓励您阅读jtahlborn的答案,至少需要一个警告。