getPassword()
方法(返回char[]
),而不是通常的getText()
方法(返回String
)。同样,我发现建议不要使用String
来处理密码。为什么
String
在涉及密码时会对安全构成威胁?
使用char[]
感觉很不方便。字符串是不可变的,一旦创建就不能改变。将密码作为字符串创建会在堆上或字符串池中留下与密码相关的引用。如果有人获取Java进程的堆转储并仔细扫描,他可能能够猜出密码。当然,这些未使用的字符串会被垃圾收集器回收,但这取决于何时GC启动。
另一方面,char[]是可变的,只要认证完成,您就可以用任何字符覆盖它们,例如所有M或反斜杠。现在,即使有人获取了堆转储,也可能无法获得当前未使用的密码。这在某种意义上为您提供了更多控制,例如自己清除对象内容而不是等待GC来执行。
字符串是不可变的并且进入字符串池。一旦写入,就无法被覆盖。
char[]
是一个数组,你应该在使用密码后覆盖它,以下是应该这样做的方法:
char[] passw = request.getPassword().toCharArray()
if (comparePasswords(dbPassword, passw) {
allowUser = true;
cleanPassword(passw);
cleanPassword(dbPassword);
passw = null;
}
private static void cleanPassword (char[] pass) {
Arrays.fill(pass, '0');
}
攻击者可能使用的一个情景是崩溃转储 - 当JVM崩溃并生成内存转储时,您将能够看到密码。
这不一定是恶意的外部攻击者。这可能是一个具有监控服务器访问权限的支持用户。他/她可以窥视崩溃转储并找到密码。
request.getPassword()
不是已经创建字符串并将其添加到池中了吗? - Tvde1ch = '0'
改变了局部变量 ch
,但对数组没有影响。而且你的例子本来就毫无意义,你首先使用一个字符串实例调用 toCharArray()
方法,创建一个新的数组,即使你正确地覆盖了新数组,它也不会改变原始的字符串实例,所以与直接使用字符串实例相比并没有任何优势。 - HolgerArrays.fill(pass, '0');
。 - Holger简短明了的答案是,char[]
是可变的,而 String
对象是不可变的。
在 Java 中,String
是不可变对象。这就是为什么一旦创建后它们就无法被修改,因此从内存中删除它们的唯一方法是进行垃圾回收。只有当对象释放的内存可以被覆盖时,数据才会消失。
现在 Java 中的垃圾回收并没有发生在任何保证间隔。因此,String
可以在内存中存在很长时间,如果在此期间进程崩溃,则字符串的内容可能会出现在内存转储或某个日志中。
使用字符数组,您可以读取密码,尽快处理完毕,然后立即更改内容。
案例字符串:
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[] 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中的字符串是不可变的。因此,每当创建字符串时,它将一直保留在内存中,直到垃圾收集器对其进行回收。因此,任何可以访问内存的人都可以读取字符串的值。
如果修改字符串的值,则会创建一个新的字符串。因此,原始值和修改后的值都会保留在内存中,直到被垃圾收集器回收。
使用字符数组,一旦密码的目的得到服务,即可修改或擦除数组内容。在修改之后甚至在垃圾收集开始之前,原始数组内容将不会在内存中找到。
由于安全问题,最好将密码存储为字符数组。
strings
和String
池(先前使用过的字符串池)都存储在Permgen中。此外,请参见5.1节:https://docs.oracle.com/javase/specs/jvms/se6/html/ConstantPool.doc.html#67960JVM始终检查Strings
是否具有相同的引用值,并会为您调用String.intern()
。结果是每当JVM检测到常量池或堆中的相同字符串时,它都会将它们移动到Permgen中。我曾经在几个应用程序上遇到了“creeping permgen”问题,直到1.7版本。这是一个真正的问题。 - avgvstvsconstant_pool
中,该池位于permgen中。如果一个字符串被多次使用,它将被放到字符串池中。 - avgvstvs