在Swing中,密码字段有一个getPassword()(returns char[])方法,而不是通常的getText()(return String)方法。同样,我也遇到了一个建议,不要使用String来处理密码。
为什么字符串对密码安全构成威胁?使用char[]感觉不方便。
在Swing中,密码字段有一个getPassword()(returns char[])方法,而不是通常的getText()(return String)方法。同样,我也遇到了一个建议,不要使用String来处理密码。
为什么字符串对密码安全构成威胁?使用char[]感觉不方便。
当前回答
字符串是不可变的,它将进入字符串池。一旦写入,就无法覆盖。
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崩溃并生成内存转储时,您将能够看到密码。
这不一定是恶意的外部攻击者。这可能是一个支持用户,可以访问服务器进行监控。他/她可以偷看垃圾堆并找到密码。
其他回答
引用一份官方文档,Java Cryptography Architecture指南介绍了char[]与String密码(关于基于密码的加密,但这更一般地是关于密码):
将密码收集并存储在对象中似乎是合乎逻辑的然而,这里有一个警告:类型String是不可变的,即没有定义允许您更改(覆盖)或清零字符串的内容使用后。此功能使字符串对象不适合存储诸如用户密码之类的安全敏感信息。你应始终在char数组。
Java编程语言4.0版安全编码指南2-2也提到了类似的内容(尽管最初是在日志记录的上下文中):
准则2-2:不记录高度敏感信息一些信息,如社会安全号码(SSN)和密码是高度敏感的。此信息不应保留无论在什么地方,甚至在管理员。例如,不应将其发送到日志文件和它的存在不应该通过搜索来检测。一些瞬态数据可以保存在可变数据结构中,例如char数组,以及使用后立即清除。清除数据结构减少了当对象移入时,在典型的Java运行时系统上的有效性存储器对程序员来说是透明的。本指南还对实施和使用没有数据语义知识的低级库他们正在处理。例如,低级字符串解析库可以记录其工作的文本。应用程序可以解析SSN与图书馆。这会导致SSN管理员可以访问日志文件。
除非您在使用后手动清理,否则char数组不会为您提供字符串,我还没有看到有人真正这样做。所以对我来说,char[]和String的偏好有点夸张。
看看这里广泛使用的SpringSecurity库,问问你自己——SpringSecurity的家伙们是无能还是字符密码根本没有意义。当一些讨厌的黑客从你的RAM中获取内存转储时,即使你使用复杂的方法隐藏密码,也要确保他/她会得到所有密码。
然而,Java一直在变化,一些可怕的特性,如Java8的字符串重复数据消除特性,可能会在您不知情的情况下实习字符串对象。但这是一个不同的对话。
字符串是不可变的,一旦创建就不能更改。将密码创建为字符串将在堆或字符串池中留下对密码的零散引用。现在,如果有人对Java进程进行堆转储并仔细扫描,他可能会猜出密码。当然,这些未使用的字符串将被垃圾收集,但这取决于GC何时启动。
另一方面,一旦完成身份验证,char[]是可变的,您可以用任何字符(如所有M或反斜杠)覆盖它们。现在,即使有人进行了堆转储,他也可能无法获取当前未使用的密码。这在某种意义上为您提供了更多的控制,比如自己清除Object内容,而不是等待GC执行。
编辑:经过一年的安全研究后,我回到了这个答案,我意识到这意味着你可能会比较明文密码。请不要。使用一个安全的单向散列和一个合理的迭代次数。考虑使用图书馆:这东西很难弄对!
原始答案:String.equals()使用短路求值,因此容易受到定时攻击,这一事实如何?这可能不太可能,但理论上可以对密码比较计时,以确定正确的字符序列。
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
// Quits here if Strings are different lengths.
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
// Quits here at first different character.
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
关于定时攻击的更多资源:
定时攻击的教训关于信息安全堆栈交换上定时攻击的讨论当然,定时攻击维基百科页面
我认为这不是一个有效的建议,但是,我至少可以猜出原因。
我认为动机是希望确保在密码被使用后,您可以及时且确定地删除记忆中的所有密码痕迹。使用char[],您可以用空白或确定的内容覆盖数组的每个元素。不能这样编辑字符串的内部值。
但这并不是一个好答案;为什么不确保对char[]或String的引用不转义?那么就没有安全问题了。但问题是,String对象在理论上可以被内部化,并在常量池中保持活动状态。我想使用char[]禁止这种可能性。