1. Overview
1.概述
In this article, we’ll explain why we should use char[] array for representing passwords instead of String in Java.
在这篇文章中,我们将解释为什么我们应该使用char[]数组来表示密码而不是在Java中使用String。
Please note that this tutorial focuses on the ways of manipulating passwords in the memory, not on the actual ways of storing them, which usually is handled in the persistence layer.
请注意,本教程的重点是在内存中操作密码的方式,而不是存储密码的实际方式,这通常是在持久层中处理。
We also assume that we can’t control the format of the password (e.g. the password comes from the 3rd party API in the form of the String). Although it’d seem obvious to use object of type java.lang.String for manipulating passwords, it’s recommended by Java team themselves to use char[] instead.
我们还假设我们无法控制密码的格式(例如,密码是以String的形式来自第三方API)。尽管使用java.lang.String类型的对象来操作密码似乎很明显,但Java团队自己建议使用char[]来代替。
For instance, if we have a look at the JPasswordField of javax.swing, we can see that the method getText() which returns String is deprecated since Java 2 and is replaced by getPassword() method which returns char[].
例如,如果我们看一下javax.swing的JPasswordField,我们可以看到返回String的getText()方法从Java 2开始被弃用,而被返回char的getPassword方法所取代。
So, let’s explore a few strong reasons why that’s the case.
因此,让我们来探讨一下为什么会这样的几个有力理由。
2. Strings Are Immutable
2.字符串是不可改变的
Strings in Java are immutable which means that we cannot change them using any high-level APIs. Any change on a String object will produce a new String, keeping the old one in memory.
Java中的String是不可变的,这意味着我们不能使用任何高层API来改变它们。对String对象的任何改变都会产生一个新的String,而将旧的保留在内存中。
Therefore, the password stored in a String will be available in memory until Garbage Collector clears it. We cannot control when it happens, but this period can be significantly longer than for regular objects since Strings are kept in a String Pool for re-usability purpose.
因此,存储在String中的密码将在内存中可用,直到垃圾收集器将其清除。我们无法控制它何时发生,但这段时间可能比普通对象的时间长得多,因为String被保存在一个String Pool中,以达到可重复使用的目的。
Consequently, anyone with access to the memory dump can retrieve the password from memory.
因此,任何能够接触到内存转储的人都可以从内存中检索到该密码。
With a char[] array instead of the String, we can explicitly wipe data after we finish with intended work. This way, we’ll ensure that password is removed from memory even before garbage collection takes place.
用char[]数组代替String,我们可以在完成预定工作后明确地擦除数据。这样,我们将确保密码在垃圾收集发生之前就从内存中被删除。
Let’s now take a look at code snippets, which demonstrate what we’ve just discussed.
现在让我们看一下代码片断,它展示了我们刚刚讨论的内容。
First for String:
首先为String。
System.out.print("Original String password value: ");
System.out.println(stringPassword);
System.out.println("Original String password hashCode: "
+ Integer.toHexString(stringPassword.hashCode()));
String newString = "********";
stringPassword.replace(stringPassword, newString);
System.out.print("String password value after trying to replace it: ");
System.out.println(stringPassword);
System.out.println(
"hashCode after trying to replace the original String: "
+ Integer.toHexString(stringPassword.hashCode()));
The output will be:
输出结果将是。
Original String password value: password
Original String password hashCode: 4889ba9b
String value after trying to replace it: password
hashCode after trying to replace the original String: 4889ba9b
Now for char[]:
现在是char[]。
char[] charPassword = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
System.out.print("Original char password value: ");
System.out.println(charPassword);
System.out.println(
"Original char password hashCode: "
+ Integer.toHexString(charPassword.hashCode()));
Arrays.fill(charPassword, '*');
System.out.print("Changed char password value: ");
System.out.println(charPassword);
System.out.println(
"Changed char password hashCode: "
+ Integer.toHexString(charPassword.hashCode()));
The output is:
输出是。
Original char password value: password
Original char password hashCode: 7cc355be
Changed char password value: ********
Changed char password hashCode: 7cc355be
As we can see, after we tried to replace the content of original String, the value remains the same and hashCode() method didn’t return a different value in the same execution of the application, meaning that the original String stayed intact.
我们可以看到,在我们试图替换原始String的内容后,值保持不变,hashCode()方法在应用程序的同一执行中没有返回不同的值,这意味着原始String保持不变。
And for the char[] array, we were able to change the data in the same object.
而对于char[]数组,我们能够改变同一对象中的数据。
3. We Can Accidentally Print Passwords
3.我们可以意外地打印密码
Another benefit of working with passwords in char[] array is the prevention of accidental logging of the password in consoles, monitors or other more or less insecure places.
在char[]数组中处理密码的另一个好处是防止在控制台、监视器或其他或多或少不安全的地方意外地记录密码。
Let’s check out the next code:
让我们来看看下一个代码。
String passwordString = "password";
char[] passwordArray = new char[]{'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
System.out.println("Printing String password -> " + passwordString);
System.out.println("Printing char[] password -> " + passwordArray);
With the output:
随着输出。
Printing String password -> password
Printing char[] password -> [C@6e8cf4c6
We see that the content itself is printed in the first case, while in the second case, the data is not so useful, which makes char[] less vulnerable.
我们看到,在第一种情况下,内容本身被打印出来了,而在第二种情况下,数据并不那么有用,这使得char[]不那么容易受到影响。
4. Conclusion
4.结论
In this quick article, we emphasized several reasons why we shouldn’t use Strings for collecting passwords and why we should use char[] arrays instead.
在这篇快速文章中,我们强调了几个原因,为什么我们不应该使用Strings来收集密码,而应该使用char[]数组。
As always, code snippets can be found over on GitHub.
像往常一样,代码片段可以在GitHub上找到。