java - 通过在垃圾收集之前使用反射来清理内容,安全地使用 String 作为密码

标签 java string security garbage-collection passwords

使用反射清理 String 是否使使用 String 与使用 char[] 作为密码一样安全?

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

但是,char[] 不如 String 方便,因此如果可以“清理”String 会很方便,如果需要,从而使 Stringchar[] 一样安全。

下面是一个使用反射将 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 产生不利影响。

此外,为了简单起见,我将方法保留为相当“原始”的状态。如果我要使用它,我不会声明抛出的异常(尝试/捕获/忽略它们)并重构重复的代码。

最佳答案

有两个潜在的安全问题:

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

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

  2. JLS ( 17.5.3 ) 警告使用反射更改 final 变量的效果是未定义的。

    但是,这是基于 Java 内存模型,以及允许编译器主动缓存 final 变量的事实。在这种情况下:

    • 您会期望 String 是线程受限的,并且

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

我不希望这些是真正的问题...模数修复的过度归零。


但真正令人担忧的是Velociraptors . :-)


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

关于java - 通过在垃圾收集之前使用反射来清理内容,安全地使用 String 作为密码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17151382/

相关文章:

java - Java 中的 MS Access 删除查询引发 java.sql.SQLException

java - 定义另一个包下的资源(主包除外)

java - 让 Joda Time 使用我的 bukkit 插件时遇到问题

PHP/MySQL 安全 我可以屏蔽与数据库的连接吗?

ios - 在 HTTP 请求中保护 API key

Java MyBatis 枚举字符串值

java - Google map 查询返回的 JSON 包含像\x26 这样的编码字符(如何解码?)

linux - 查找字符串并添加字符串

c# - 如何在不区分大小写的模式下使用 HashSet<string>.Contains() 方法?

ios - Firebase .plist 配置文件和安全性