16

使用反射擦洗String使用与使用密码String一样安全吗?char[]

从安全方面来看,通常认为最好的做法是char[]用于存储/传递密码,因为可以在代码中尽快将其内容清零,这可能在垃圾收集清理它并重用内存之前很重要(擦除所有痕迹),限制内存攻击的时间窗口。

但是,它char[]不如. _StringStringStringchar[]

下面是一种使用反射将String.

这种方法是否“OK”,是否达到了使密码与密码String一样安全的目标char[]

public static void scrub(String str) throws NoSuchFieldException, IllegalAccessException {
    Field valueField = String.class.getDeclaredField("value");
    Field offsetField = String.class.getDeclaredField("offset");
    Field countField = String.class.getDeclaredField("count");
    Field hashField = String.class.getDeclaredField("hash");
    valueField.setAccessible(true);
    offsetField.setAccessible(true);
    countField.setAccessible(true);
    hashField.setAccessible(true);
    char[] value = (char[]) valueField.get(str);
    // overwrite the relevant array contents with null chars
    Arrays.fill(value, offsetField.getInt(str), countField.getInt(str), '\0');
    countField.set(str, 0); // scrub password length too
    hashField.set(str, 0); // the hash could be used to crack a password
    valueField.setAccessible(false);
    offsetField.setAccessible(false);
    countField.setAccessible(false);
    hashField.setAccessible(false);
}

这是一个简单的测试:

String str = "password";
scrub(str);
System.out.println('"' + str + '"');

输出:

""

注意:您可以假设密码不是String常量,因此调用此方法不会对实习字符串产生不利影响。

另外,为了简单起见,我已经离开了这个方法是一个相当“原始”的状态。如果我要使用它,我不会声明抛出的异常(尝试/捕获/忽略它们)并重构重复的代码。

4

2 回答 2

7

有两个潜在的安全问题:

  1. String 可以与其他 String 共享其支持数组;例如,如果String是通过调用substring更大的String. 因此,当您将整个value数组归零时,您可能会覆盖其他字符串的状态......不包含密码。

    解决方法是仅将密码字符串使用的后备数组部分归零。

  2. JLS ( 17.5.3 ) 警告说,使用反射来改变final变量的效果是不确定的。

    然而,这个上下文是 Java 内存模型,以及允许编译器积极缓存final变量的事实。在这种情况下:

    • 你会期望 String 是线程限制的,并且

    • 你不应该再次使用这些变量中的任何一个。

我不认为这些都是真正的问题......模修正过度激进的归零value


但真正令人担忧的是迅猛龙。:-)


我很困惑,您实际上会费心像这样破解密码。当您考虑它时,您要防止的是有人可以读取进程内存......或核心转储或交换文件......以检索密码的可能性。但是,如果有人可以做到这一点,那么您的系统安全性必须已经受到损害......因为这些东西很可能需要root访问(或等效)。如果他们有权访问,他们可以“调试”您的程序并在您的应用程序破坏密码之前root捕获密码。

于 2013-06-17T15:45:43.077 回答
1

我反对 String 的一个论点是,无意中复制太容易了。理论上安全地使用字符串是可能的,但是整个库生态系统都基于这样一个假设:复制字符串是完全可以的。最后,考虑到所有的限制,对于这个用例,字符串可能不像通常那样方便。

于 2013-06-17T15:43:24.867 回答