在Swing中,密码字段有一个getPassword()(returns char[])方法,而不是通常的getText()(return String)方法。同样,我也遇到了一个建议,不要使用String来处理密码。
为什么字符串对密码安全构成威胁?使用char[]感觉不方便。
在Swing中,密码字段有一个getPassword()(returns char[])方法,而不是通常的getText()(return String)方法。同样,我也遇到了一个建议,不要使用String来处理密码。
为什么字符串对密码安全构成威胁?使用char[]感觉不方便。
当前回答
字符数组(char[])可以在使用后通过将每个字符设置为零而不设置为字符串来清除。如果有人能够以某种方式看到内存映像,那么如果使用字符串,他们可以看到纯文本的密码,但是如果使用char[],则在用0清除数据后,密码是安全的。
其他回答
正如Jon Skeet所说,除了使用反射,没有其他方法。
然而,如果反射是您的选择,您可以这样做。
public static void main(String[] args) {
System.out.println("please enter a password");
// don't actually do this, this is an example only.
Scanner in = new Scanner(System.in);
String password = in.nextLine();
usePassword(password);
clearString(password);
System.out.println("password: '" + password + "'");
}
private static void usePassword(String password) {
}
private static void clearString(String password) {
try {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] chars = (char[]) value.get(password);
Arrays.fill(chars, '*');
} catch (Exception e) {
throw new AssertionError(e);
}
}
运行时
please enter a password
hello world
password: '***********'
注意:如果String的char[]作为GC循环的一部分被复制,那么前一个副本可能在内存中的某个位置。
这个旧副本不会出现在堆转储中,但如果您可以直接访问进程的原始内存,您可以看到它。通常,您应该避免任何人有这样的访问权限。
字符串是不可变的。这意味着一旦创建了String,如果另一个进程可以转储内存,那么在垃圾收集开始之前,就没有办法(除了反射之外)清除数据。
使用数组,您可以在完成后显式擦除数据。您可以用任何您喜欢的内容覆盖数组,并且密码不会出现在系统中的任何位置,甚至在垃圾收集之前。
是的,这是一个安全问题,但即使使用char[]也只会减少攻击者的机会窗口,而且只针对这种特定类型的攻击。
如注释中所述,垃圾收集器移动的数组可能会在内存中留下数据的零散副本。我认为这是特定于实现的——垃圾收集器可以在运行时清除所有内存,以避免这种情况。即使这样,char[]仍有一段时间包含实际字符作为攻击窗口。
大小写字符串:
String password = "ill stay in StringPool after Death !!!";
// some long code goes
// ...Now I want to remove traces of password
password = null;
password = "";
// above attempts wil change value of password
// but the actual password can be traced from String pool through memory dump, if not garbage collected
案例CHAR阵列:
char[] passArray = {'p','a','s','s','w','o','r','d'};
// some long code goes
// ...Now I want to remove traces of password
for (int i=0; i<passArray.length;i++){
passArray[i] = 'x';
}
// Now you ACTUALLY DESTROYED traces of password form memory
字符串是不可变的,一旦创建就不能更改。将密码创建为字符串将在堆或字符串池中留下对密码的零散引用。现在,如果有人对Java进程进行堆转储并仔细扫描,他可能会猜出密码。当然,这些未使用的字符串将被垃圾收集,但这取决于GC何时启动。
另一方面,一旦完成身份验证,char[]是可变的,您可以用任何字符(如所有M或反斜杠)覆盖它们。现在,即使有人进行了堆转储,他也可能无法获取当前未使用的密码。这在某种意义上为您提供了更多的控制,比如自己清除Object内容,而不是等待GC执行。
简单明了的答案是因为char[]是可变的,而String对象不是可变的。
Java中的字符串是不可变的对象。这就是为什么它们一旦创建就不能被修改,因此从内存中删除它们的内容的唯一方法就是垃圾收集。只有当对象释放的内存可以被覆盖时,数据才会消失。
现在,Java中的垃圾收集不会在任何保证的时间间隔内发生。因此,字符串可以在内存中保存很长时间,如果在此期间进程崩溃,字符串的内容可能会在内存转储或某些日志中结束。
使用字符数组,您可以读取密码,尽快完成处理,然后立即更改内容。