通过使用反射在垃圾回收之前擦除内容,安全地使用String作为密码

Boh*_*ian 14 java security string passwords garbage-collection

使用反射来擦除Stringmake使用String和使用char[]密码一样安全吗?

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

但是,char[]并不方便String,所以String如果需要的话可以"擦洗",这样会很方便,因此String安全如此char[].

下面是一个使用反射将字段清零的方法String.

这种方法是否"正常",它是否达到了使密码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);
}
Run Code Online (Sandbox Code Playgroud)

这是一个简单的测试:

String str = "password";
scrub(str);
System.out.println('"' + str + '"');
Run Code Online (Sandbox Code Playgroud)

输出:

""
Run Code Online (Sandbox Code Playgroud)

注意:您可能认为密码不是String常量,因此调用此方法对interned Strings没有任何负面影响.

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

Ste*_*n C 7

有两个潜在的安全问题:

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

    解决方法是仅将密码字符串使用的后备阵列部分归零.

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

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

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

    • 你不应该再使用任何这些变量.

我不认为这些中的任何一个都是真正的问题...模数固定过度积极的归零value.


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


我很困惑你真的很想打扰这样的密码.当你考虑它时,你所保护的是有人可以读取进程内存......或者核心转储或交换文件......来检索密码.但是,如果有人能做到这一点,那么你的系统安全性必须已经受到损害......因为这些事情很可能需要root访问(或等效).如果他们有权root访问,他们可以"调试"您的程序并应用程序删除之前捕获密码.