You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Java.io.Console读取密码后内存Dump仍泄露密码的原因排查

为什么覆盖密码字符数组后内存Dump仍能看到密码?

我基于Java官方教程的代码改编了如下程序,目的是通过char[]存储密码并使用Arrays.fill覆盖来避免内存中残留明文,但执行后用jmap生成内存Dump,仍能找到输入的密码明文。具体代码及场景如下:

public class RedirectOutputStreamExample {
    public static void main(String[] args) throws InterruptedException {
        Console c = System.console();
        if (c == null) {
            System.err.println("No console.");
            System.exit(1);
        }

        String login = ((Console) c).readLine("Enter your login: ");
        char [] oldPassword = c.readPassword("Enter your old password: ");

        if (verify(login, oldPassword)) {
            boolean noMatch;
            do {
                char [] newPassword1 = c.readPassword("Enter your new password: ");
                char [] newPassword2 = c.readPassword("Enter new password again: ");
                noMatch = ! Arrays.equals(newPassword1, newPassword2);
                if (noMatch) {
                    c.format("Passwords don't match. Try again.%n");
                } else {
                    c.format("Password for %s changed.%n", login);
                }
                Arrays.fill(newPassword1, ' ');
                Arrays.fill(newPassword2, ' ');
                System.out.println("password is: "+newPassword1);
                System.out.println("password is: "+newPassword2);
            } while (noMatch);
        }
        Arrays.fill(oldPassword, ' ');
        System.out.println(oldPassword);
        Thread.sleep(60000);
    }
}

程序中三个密码字符数组被覆盖后,打印结果为空行(符合预期),操作在原生控制台完成(IntelliJ终端因安全限制报错"No Console")。但生成内存Dump后,仍能在其中找到输入的密码明文。


核心原因:Console内部实现可能将密码暂存为String

这既不是操作错误,也不是JVM安全漏洞,核心问题在于**Console.readPassword()的内部实现可能会将用户输入的密码临时存储为String对象**。

虽然你在代码里用char[]存储并主动覆盖,但Console类的底层输入处理逻辑中,部分JDK实现会先把输入内容转成String缓存。而String是Java中的不可变对象,一旦创建就无法修改其内部字符数组,即便你自己维护的char[]被覆盖,这些临时生成的String实例仍会留在内存中,直到被垃圾回收机制清理。

内存Dump是对JVM内存状态的快照,若生成Dump时这些临时String还未被GC回收,就能在快照中找到密码明文。

额外注意事项

  • 代码中System.out.println("password is: "+newPassword1)会触发char[]的toString()方法,生成类似[C@xxxx的标识字符串,不会泄露密码,但要绝对避免主动将char[]转换为String(比如new String(oldPassword)),否则会直接创建不可变的密码字符串实例。
  • 即便使用char[],也无法完全消除内存中密码残留的可能性——JVM的即时编译、内存拷贝等底层操作可能会在内存中留下数据副本,但这类残留比String导致的问题更难追踪和处理。

内容的提问来源于stack exchange,提问作者Yoo oo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 15:15:00