为什么char 首选string密码?

在Swing中,密码字段有一个getPassword() (返回char[] )方法,而不是通常的getText() (返回String )方法。 同样,我遇到了一个不使用String来处理密码的build议。

为什么String在密码方面对安全构成威胁? 使用char[]感觉不方便。

string是不可改变的 。 这意味着,一旦你创build了String ,如果另一个进程可以转储内存,没有办法(除了反思 ),你可以摆脱垃圾收集踢的数据。

使用数组,您可以在完成数据之后清晰地擦除数据。 你可以用你喜欢的任何东西覆盖这个数组,甚至在垃圾收集之前密码也不会出现在系统的任何地方。

所以是的,这一个安全问题 – 但是即使使用char[]只能减less攻击者的机会窗口,而且这只是针对这种特定types的攻击。

正如在注释中指出的那样,被垃圾收集器移动的数组可能会将数据的杂散副本留在内存中。 我相信这是针对具体实现的 – 垃圾收集器可以清除所有内存,避免这种情况。 即使如此, char[]中仍然会包含实际字符作为攻击窗口。

虽然这里的其他build议似乎是有效的,但还有一个很好的理由。 用普通的String你有更大的机会意外地打印到日志 ,监视器或其他一些不安全的地方的密码char[]不易受攻击。

考虑这个:

 public static void main(String[] args) { Object pw = "Password"; System.out.println("String: " + pw); pw = "Password".toCharArray(); System.out.println("Array: " + pw); } 

打印:

 String: Password Array: [C@5829428e 

引用一个官方的文档, Java密码体系结构指南对此进行了说明,这是关于char[]String密码(关于基于密码的encryption,但更一般的密码当然是):

将密码收集并存储在java.lang.Stringtypes的对象中似乎是合乎逻辑的。 但是,这里需要注意的是: Stringtypes的Object是不可变的,也就是说,没有定义方法可以让你在使用之后改变(覆盖)或者将内容清零。 此function使得String对象不适合存储安全敏感信息(如用户密码)。 你应该总是收集和存储安全敏感的信息在一个char数组。

Java编程语言4.0版安全编码指南的准则2-2也提到了类似的内容(虽然它最初是在日志的上下文中):

准则2-2:不要logging高度敏感的信息

一些信息,如社会安全号码(SSN)和密码,是非常敏感的。 这些信息不应该被保存超过必要的时间,也不应该被pipe理员看到。 例如,它不应该被发送到日志文件,它的存在不应该通过search检测。 一些瞬态数据可能保存在可变数据结构中,如char数组,并在使用后立即清除。 清除数据结构已经降低了典型的Java运行时系统的效率,因为对象被透明地存储在内存中给程序员。

本指南还对实施和使用对所处理数据没有语义知识的低级库有影响。 作为一个例子,一个低级的stringparsing库可以logging它所处理的文本。 一个应用程序可能会parsing一个SSN的库。 这创build了SSN可供pipe理员访问日志文件的情况。

字符数组( char[] )可以通过将每个字符设置为零而不使用string来清除。 如果某人能够以某种方式看到内存映像,那么如果使用了string,他们可以以纯文本的方式看到密码,但是如果使用了char[] ,那么在用0清除数据之后,密码是安全的。

有些人认为一旦不再需要密码,就必须覆盖用来存储密码的内存。 这减less了攻击者从系统中读取密码的时间窗口,并完全忽略了攻击者已经需要足够的访问权来劫持JVM内存的事实。 有这么多访问权限的攻击者可以捕获你的关键事件,使其完全无用(如果我错了,请纠正我)。

更新

感谢评论我必须更新我的答案。 显然有两种情况可以增加一个(非常)小的安全性改进,因为它减less了密码可能落在硬盘上的时间。 不过,我认为这对大多数用例来说都是过度的。

  • 您的目标系统可能configuration错误,或者您必须假定它是,并且您必须对核心转储(如果系统不由pipe理员pipe理,则可以是有效的)偏执。
  • 您的软件必须过于偏执,以防止攻击者访问硬件时发生数据泄露 – 使用TrueCrypt (已停用), VeraCrypt或CipherShed等 。

如果可能,禁用核心转储和交换文件将会照顾到这两个问题。 但是,他们需要pipe理员权限,可能会减lessfunction(使用较less的内存),并从正在运行的系统中提取RAM仍然是一个值得关注的问题。

  1. 如果将密码存储为纯文本,则string在Java 中不可变 ,直到垃圾收集器清除它为止,并且由于string在string池中用于重新使用,因此很可能会长时间保留在内存中持续时间,这构成安全威胁。 由于任何有权访问内存转储的人都可以以明文formsfind密码
  2. Java推荐使用JPasswordField的getPassword()方法返回一个char []和不赞成的getText()方法,它以明文forms返回密码,说明安全原因。
  3. toString()在日志文件或控制台中始终存在打印纯文本的风险,但是如果使用Array,将不会打印数组的内容,而是打印其内存位置。

     String strPwd = "passwd"; char[] charPwd = new char[]{'p','a','s','s','w','d'}; System.out.println("String password: " + strPwd ); System.out.println("Character password: " + charPwd ); 

    string密码:passwd

    字符密码:[C @ 110b2345

最后的想法:尽pipe使用char []不够,你需要擦除内容更安全。 我还build议使用散列或encryption密码而不是纯文本,并在authentication完成后立即从内存中清除它。

我不认为这是一个有效的build议,但我至less可以猜出原因。

我认为这样做的动机是希望确保在使用后可以及时并确定地清除内存中的所有密码。 用char[]你可以覆盖数组中的每个元素的空白或肯定的东西。 您不能以这种方式编辑String的内部值。

但是,这本身并不是一个好的答案。 为什么不只是确保对char[]String的引用不能转义? 那么没有安全问题。 但是, String对象可以在理论上实现并保持在常量池中。 我想用char[]禁止这种可能性。

答案已经给出了,但我想分享一个我最近发现的与Java标准库有关的问题。 虽然他们现在非常小心地用char[]replace密码string(这当然是件好事),但是从内存清除它时,其他安全性关键数据似乎被忽略了。

我在考虑PrivateKey类。 考虑一个场景,你可以从PKCS#12文件中加载一个私有的RSA密钥,用它来执行一些操作。 现在在这种情况下,只要密钥文件的物理访问受到适当的限制,单独嗅探密码就无济于事。 作为攻击者,如果直接获取密钥而不是密码,会更好。 所需的信息可以泄露多方面,核心转储,debugging会话或交换文件只是一些例子。

事实certificate,没有任何东西可以让你从内存中清除私钥的私有信息,因为没有API可以让你擦除形成相应信息的字节。

这是一个糟糕的情况,因为本文描述了这种情况如何被潜在利用。

例如,OpenSSL库会在私钥被释放之前覆盖关键内存部分。 由于Java是垃圾收集的,因此我们需要显式的方法来擦除和使Java密钥的私有信息失效,这些私钥信息在使用密钥之后立即被应用。

正如Jon Skeet所说,除了用reflection之外,没有办法。

但是,如果反思是你的select,你可以做到这一点。

 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周期的一部分被复制,那么前一个副本就有可能在内存中。

这个旧副本不会出现在堆转储中,但是如果您可以直接访问该进程的原始内存,则可以看到它。 一般来说,你应该避免任何人有这样的访问。

这些都是原因,应该selectchar []数组而不是String作为密码。

1.由于string在Java中是不可变的,所以如果将密码作为纯文本存储,它将在内存中可用,直到垃圾收集器清除它为止,并且由于string池中的string用于重用,存在很长时间持续时间,这构成安全威胁。 由于任何有权访问内存转储的人都可以以明文方式查找密码,这也是您应该始终使用encryption密码而不是纯文本的另一个原因。 由于string是不可变的,所以没有办法改变string的内容,因为任何改变都会产生新的string,而如果你使用char [],你仍然可以将所有的元素设置为空白或零。 因此,在字符数组中存储密码明显减轻了窃取密码的安全风险。

2. Java本身build议使用JPasswordField的getPassword()方法,它返回一个char []和不赞成的getText()方法,它以明文forms返回密码,说明安全原因。 善于遵循Java团队的build议,坚持标准而不是违背标准。

3.使用string总是有在日志文件或控制台中打印纯文本的风险,但如果使用数组,则不会打印数组的内容,而是打印其内存位置。 虽然不是一个真正的理由,但仍然有道理。

  String strPassword="Unknown"; char[] charPassword= new char[]{'U','n','k','w','o','n'}; System.out.println("String password: " + strPassword); System.out.println("Character password: " + charPassword); String password: Unknown Character password: [C@110b053 

参考: http : //javarevisited.blogspot.com/2012/03/why-character-array-is-better-than.html希望这有助于。

编辑:经过一年的安全研究后,回到这个答案,我意识到它会让你相信比较明文密码的相当不幸的暗示。 请不要。 使用一个安全的单向散列与盐和合理数量的迭代 。 考虑使用库:这东西很难得到正确的!

原始回答:那么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; } 

一些更多的计时攻击资源:

  • 定时攻击课程
  • 关于在信息安全堆栈交换上的定时攻击的讨论
  • 当然, 定时攻击维基百科页面

没有什么是字符数组给你vsstring,除非你在使用后手动清理它,而且我还没有看到有人真的这样做。 所以对我来说,char [] vs String的偏好有点夸张。

看看这里 广泛使用的 Spring Security库,问问你自己 – Spring Security的人是无能的还是char()密码没有多大意义。 当一些讨厌的黑客抓住你的RAM的内存转储时,确保她会得到所有的密码,即使你使用复杂的方式来隐藏它们。

但是,Java一直在变化,而Java 8的String Deduplicationfunction等一些可怕的function可能会在您不知情的情况下实习String对象。 但这是不同的谈话。

Strings are immutable and cannot be altered once they have been created. Creating a password as a string will leave stray references to the password on the heap or on the String pool. Now if someone takes a heap dump of the Java process and carefully scans through he might be able to guess the passwords. Of course these non used strings will be garbage collected but that depends on when the GC kicks in.

On the other side char[] are mutable as soon as the authentication is done you can overwrite them with any character like all M's or backslashes. Now even if someone takes a heap dump he might not be able to get the passwords which are not currently in use. This gives you more control in the sense like clearing the Object content yourself vs waiting for the GC to do it.

Simple and short answer would be because char[] are mutable while String objects are not.

Strings in Java are immutable objects. That is why they can't be modified once created, and therefore the only way for their contents to be removed from memory is to have them garbage collected. It will be only then, when the memory freed by the object can be overwritten and the data will be gone.

Now garbage collection in Java doesn't happen at any kind of guaranteed interval. The String can thus persist in memory for a long time, and if a process crashes during this time, the contents of the string may end up in a memory dump or some log.

With a character array , you can read the password, finish working with it as soon as you can, and then immediately change the contents.

String in java is immutable. So whenever a string is created, it will remain in the memory until it is garbage collected. So anyone who has access to the memory can read the value of the string.
If the value of the string is modified then it will end up creating a new string. So both the original value and the modified value stay in the memory until it is garbage collected.

With the character array, the contents of the array can be modified or erased once the purpose of the password is served. The original contents of the array will not be found in memory after it is modified and even before the garbage collection kicks in.

Because of the security concern it is better to store password as a character array.

1) Since Strings are immutable in Java if you store password as plain text it will be available in memory until Garbage collector clears it and since String are used in String pool for reusability there is pretty high chance that it will be remain in memory for long duration, which pose a security threat. Since any one who has access to memory dump can find the password in clear text and that's another reason you should always used an encrypted password than plain text. Since Strings are immutable there is no way contents of Strings can be changed because any change will produce new String, while if you char[] you can still set all his element as blank or zero. So Storing password in character array clearly mitigates security risk of stealing password.

2) Java itself recommends using getPassword() method of JPasswordField which returns a char[] and deprecated getText() method which returns password in clear text stating security reason. Its good to follow advice from Java team and adhering to standard rather than going against it.