If you are a Java programmer, you may have been incurring an insecure practice without knowing. We all know (or should know) that is not safe to store unencrypted passwords in the database because that might compromise the protection of data at rest. But that is not the only issue, if at any time in our code there is an unencrypted password or sensitive data stored in a String variable even if it is temporary, then there could be a risk.
Why is there a risk?
String objects were not created to store passwords, they were designed to optimize space in our program. String objects in Java are “immutable” which means that after you create a String object and assign it some value, afterward you cannot remove the value nor modify it. I know you might be thinking that this is not true because you can assign “Hello World” to a given String object and in the following line assign it with “Goodbye, cruel world”, and that is technically correct. The problem is that the “Hello World” that you created first is going to keep living in the String **** even if you cannot see it.
What is the String ****?
Java uses a special memory area called the String **** to store String literals. When you create a String literal, Java checks the String **** first to see if an identical String already exists. If it does, Java will reuse the reference to the existing String, saving memory. This means that if you create 25.000 String objects and all of them have the value of “Michael Jackson” only one String Literal will be stored in memory and all variables will be pointing to the same one, optimizing the space in memory.
Ok, the object is in the String ****, where is the risk?
The String Object will remain in memory for some time before being deleted by the garbage collector. If an attacker has access to the content of the memory, they could obtain the password stored there.
Let’s see a basic example of this. The following code is creating a String object and assigning it with a secret password: “¿This is a secret password”. Then, that same object is overwritten 3 times, and the Instances Inspector of the Debugger will help us in locating String objects starting with the character “¿”.
Example 1 Code:
Example 1 Debugger:
As you can notice in the image when the debugger has gotten to the line 8, even after having changed three times the value of the String variable “a” and setting it to null at the end, all previous values remain in the memory, included our: “¿This is a secret password”.
Got it. Just avoiding creating String variables will solve the problem, right?
It is not that simple. Let us consider a second example. Now we are smarter, and we are going to use a char array to store the password instead of the String to avoid the issue of having it saved in the String ****. In addition, rather than having the secret password as literal in the code, it will be available unencrypted in a text file, which by the way is not recommended to save it unencrypted, but we will do it for this example. A BufferedReader is going to support reading the contents of the file.
Unfortunately, as you will see, password also exist in the String ****.
Example 2 Code:
Example 2 Debugger:
This case is even more puzzling because in the code a String Object was never created, at least explicitly. The problem is that the BufferedReader.readLine() is returning a String Object temporarily and the content with the unencrypted password will remain in the String ****.
What can I do to solve this problem?
In this last example we will have the unencrypted password stored in a text file, we will use a BufferedReader to read the contents of the file, but instead of using the method BufferedReader.readLine() that returns a String we are using the method BufferedReader.read() that stores the content of the file in a char array. As seen in the debugger’s screenshot, this time the file’s contents are not available in the String ****.
Example 3 Code:
Example 3 Debugger:
In summary
To solve this problem, consider following the principles listed below:
- Do not create String literals with confidential information in your code.
- Do not store confidential information in String objects. You can use other types of Objects to store this information such as the classic char array. After processing the data make sure to overwrite the char array with zeros or some random chars, just to confuse attackers.
- Avoid calling methods that will return the confidential information as String, even if you will not save that into a variable.
- Consider applying an additional security layer by encrypting confidential information. The SealedObject in Java is a great alternative to achieve this. The SealedObject is a Java Object where you can store sensitive data, you provide a secret key, and the Object is encrypted and serialized. This is useful if you want to transmit it and ensure the content remains unexposed. Afterward, you can decrypt it using the same secret key. Just one piece of advice, after decrypting it, please do not store it on a String object.