5

There is a Security note in the javadoc for java.io.Console class:

Security note: If an application needs to read a password or other secure data, it should use readPassword() or readPassword(String, Object...) and manually zero the returned character array after processing to minimize the lifetime of sensitive data in memory.

 Console cons;
 char[] passwd;
 if ((cons = System.console()) != null &&
     (passwd = cons.readPassword("[%s]", "Password:")) != null) {
     ...
     java.util.Arrays.fill(passwd, ' ');
 }

I do not understand why do you need such drastic measures? When the method that reads the password pops out of the stack, the array object referenced by the passwd local variable will be eligible for garbage collection. No one (even an attacker) could obtain a reference to that array, assuming that the array does not escape the method scope.

So why do you need to modify the array (erasing the password), when you know that it will be eligible for GC once the method pops out of the stack? They say:

to minimize the lifetime of sensitive data in memory

but for me this style of programming seems rather...desperate.

Alex
  • 971
  • 1
  • 9
  • 23
  • My first thought is that you're never guaranteed when the GC will run; could be longer than you think, hypothetically, so the idea is to avert that potential problem by erasing the password data as soon as possible. – Paul Richter Dec 26 '14 at 21:06
  • Anything in memory can be "read", in order to try and protect the sensitive data, you should wipe it/override it to reduce the risk of it been read – MadProgrammer Dec 26 '14 at 21:07
  • In memory dumps I cannot see the objects referenced by method local variables. I also cannot see unreferenced objects (i.e. eligible for GC). I only can inspect directly reachable objects. – Alex Dec 26 '14 at 21:25

4 Answers4

12

Just because object is eligible for garbage collection doesn't mean that it will be garbage collected right away. And in that period of time before the garbage collection is actually executed the attacker might get a heap memory dump for instance from which they could retrieve the password.

By zeroing it out that window of opportunity is minimized.

Edit: practical experiment:

Create following Java program:

public class Main {
    private static void readPassword() {
        char[] password = System.console().readPassword();
    }

    public static void main(String[] args) throws Exception {
        readPassword();
        Thread.sleep(1000 * 3600);
    }
}
  1. javac Main.java
  2. java Main
  3. Enter password (for instance topsecret), hit enter

Next open another terminal, find out the PID of the process (let's say it's 1000) and create a heap dump using jmap:

jmap -dump:format=b,file=dump.bin 1000

Install and open VisualVM profiler, go to File/Load and select the heap dump you just created.

Next go to the OQL console and run the following query:

select a from char[] a where a.length == 9 && a[0] == 't'

As you see in the attached screenshot, array containing "topsecret" was found, even though at the point heap dump was taken there was no accessible reference to that array. So that proves that objects even if they were referenced locally stay on the heap until garbage collected.

VisualVM

Now if I were to null-out the array and try the whole procedure again, the array containing the password will not be found.

Bohuslav Burghardt
  • 33,626
  • 7
  • 114
  • 109
  • I recognize that I did not get many memory dumps. But for the ones that I did, I could notice there only objects that are directly reachable. I do not understand how one can see the contents of an object referenced by a method local variable? – Alex Dec 26 '14 at 21:20
  • @Alex I updated my answer. Hope that clears things up. TLDR: objects stay on the heap even if they are no longer referenced from anywhere until they are garbage collected – Bohuslav Burghardt Dec 26 '14 at 22:50
  • 1
    Uuuu...nasty thing. I knew that objects still live on the heap even if they are not referenced, but I thought that you cannot read memory contents if you do not have a reference/pointer to that memory block. I did not knew that you can query memory in such way... – Alex Dec 29 '14 at 10:48
8

If I'm an attacker and can cause a heap dump to happen before that array is garbage collected, I can examine its contents. There's no guarantee that the GC will run "soon" (or at all) and if you have something very sensitive, you might not want it showing up on disk in a heap dump, no matter how slight the odds.

Todd
  • 30,472
  • 11
  • 81
  • 89
2

This question reminds me of the discussion in this popular answer from Why is char[] preferred over String for passwords?

As noted in comments, it's possible that arrays being moved by the garbage collector will leave stray copies of the data in memory. I believe this is implementation-specific - the GC may clear all memory as it goes, to avoid this sort of thing. Even if it does, there's still the time during which the char[] contains the actual characters as an attack window.

Community
  • 1
  • 1
Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
2

When the method that reads the password pops out of the stack, the array object referenced by the passwd local variable will be eligible for garbage collection. No one (even an attacker) could obtain a reference to that array, assuming that the array does not escape the method scope.

Alas, it is not quite this simple: The Java language gives no guarantees how soon a garbage collection will occur, and it may be possible for an attacker to read the memory in the mean time.

to minimize the lifetime of sensitive data in memory

This advice obviously targets a threat model where the attacker has intermittent read access to heap memory, and we must still do our best to make it harder (but not necessarily impossible) for the attacker to aquire sensitive data.

Whether you have this threat model is something only you can assess, but it not as absurd as it may seem at first glance, because the various systems involved do not necessarily guarantee the privacy of main memory, for instance:

  • JVM

    • a heap dump also contains unreachable objects (some tools filter these when opening the dump file, but they are present in the file and can be recovered)
    • most garbage collectors copy the live objects from the old to the new memory space. These copies may outlive the object itself, and show up in a dump of the process' memory.
  • operating system

    • the operating system's virtual memory manager may elect to swap memory to disk, where it may remain after shut down, and recovered (for instance by booting into another operating system)
    • the operating system's hibernation feature also writes main memory to disk
    • some operating system's virtual memory manager do not clear physical memory when reassigning it to another process. That other process can then read the data left behind by the previous process.
    • the operating system may allow a process to debug another, giving one process access to another process' memory.
  • hypervisor

    • when the hypervisor takes a snapshot of a VM, it writes virtual main memory to the disk. If the attacker gets access to that snapshot ...
  • hardware

    • the main memory of the system may retain state even after power off, enabling the data to be inspected from another operating system or computer. Perhaps suprisingly, most modern DRAM chips fall into this category (source)
    • and of course, hardware could contain a back door, as reported for Cisco's routers

That said, most of these attacks presume a level of access that would enable far more serious attacks, and should therefore be prevented anyway, in which case reducing the life time of sensitive data in memory is rather redundant. Personally, I would not defend data but storing it in memory for as short a time as possible, but defend the privacy of memory by using trusted hardware, physically secured against tampering, secure operating systems configured by professionals, with os-level access restricted as far as practical, and secure storage for backups and diagnostic dumps of any kind.

Therefore, it is my opinion that the use of char[] for passwords is rarely useful. Incidentally, this opinion appears to be shared by the designers of the JDBC API, which requires connection passwords to be passed as String, preventing them from being cleared after the connection is established.

Nevertheless, there may be cases where the privacy of main memory can not be guaranteed, and damage mitigation by keeping the window of vulnerability as small as possible is appropriate. Your threat model should answer whether this is such a case.

meriton
  • 68,356
  • 14
  • 108
  • 175