使用反射清理 String
是否使使用 String
与使用 char[]
作为密码一样安全?
从安全的角度来看,通常认为使用 char[]
来存储/传递密码是最佳实践,因为可以在代码中尽快将其内容置零,这可能是在垃圾收集清理它并重新使用内存(删除所有痕迹)之前显着,限制了内存攻击的时间窗口。
但是,char[]
不如 String
方便,因此如果可以“清理”String
会很方便,如果需要,从而使 String
与 char[]
一样安全。
下面是一个使用反射将 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
常量,因此调用此方法不会对 interned String 产生不利影响。
此外,为了简单起见,我将方法保留为相当“原始”的状态。如果我要使用它,我不会声明抛出的异常(尝试/捕获/忽略它们)并重构重复的代码。
最佳答案
有两个潜在的安全问题:
字符串可以与其他字符串共享其支持数组;例如如果
String
是通过在更大的String
上调用substring
创建的。因此,当您将整个value
数组归零时,您可能会覆盖其他不包含密码的字符串的状态。解决方法是仅将密码字符串使用的后备数组部分归零。
JLS ( 17.5.3 ) 警告使用反射更改
final
变量的效果是未定义的。但是,这是基于 Java 内存模型,以及允许编译器主动缓存
final
变量的事实。在这种情况下:您会期望 String 是线程受限的,并且
您不应该再次使用这些变量中的任何一个。
我不希望这些是真正的问题...模数修复值
的过度归零。
但真正令人担忧的是Velociraptors . :-)
令我感到困惑的是,您居然会费心像这样破解密码。当您考虑它时,您要防止的是有人可以读取进程内存的可能性......或核心转储或交换文件......以检索密码。但是,如果有人可以做到这一点,那么您的系统安全性肯定已经受到威胁……因为这些东西很可能需要 root
访问权限(或同等权限)。如果他们有 root
访问权限,他们可以“调试”您的程序并在您的应用程序销毁它们之前获取密码。
关于java - 通过在垃圾收集之前使用反射来清理内容,安全地使用 String 作为密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17151382/