1

Context

I recently had a cryptography lecture and we discussed about persistence of critical elements in memory. Typically, C/C++ library Libsodium suggests to clear any buffer that contained sensitive information, such as a secret (ref). I know that GuardedString is backed by an array of bytes and the docs recommends to call the method dispose once the stored secret is no longer used, that fills the array of bytes using Arrays.fill.

Question

Does the JVM guarantee that the values of the byte array are gone when overwritten or may the original values remain in memory under certain conditions ? For example, unused/unreferenced Strings are conserved in the Java String Pool until a garbage collection is triggered. Are there similar caching or mechanisms for other types such as the byte array that can compromise the secret that should be disposed from the GuardedString ? Any reference in the specs of the JVM ?

Many thanks !

Jämes
  • 6,945
  • 4
  • 40
  • 56
  • See https://stackoverflow.com/a/121798/1531124 for example. In theory, writing nulls into arrays should be good enough. – GhostCat Apr 03 '19 at 19:48
  • *Does the JVM guarantee...* No, the JVM does not yet implement that kind of secure memory. – President James K. Polk Apr 03 '19 at 22:29
  • 1
    @GhostCat *in theory*, a JVM can eliminate any write that is never followed by a read, i.e. made to an object that is abandoned afterwards. Further, due to copying garbage collectors, an unknown number of copies may already fly around in the heap memory. *In practice*, this zero filling may be good enough, even if there is no real guaranty. – Holger Apr 04 '19 at 09:03
  • @ZenLulz the String pool is irrelevant here. A password string wouldn’t get into the pool, unless you are putting it explicitly (which you normally don’t). But regardless of whether it is in the pool or not, all objects stay in memory until a garbage collection is triggered. And even after a garbage collection, their contents may stay in memory for an unknown time, until it gets overwritten for an actual new allocation reusing this particular memory. – Holger Apr 04 '19 at 09:08
  • @Holger The write could be followed by a read, and maybe another write ;-) – GhostCat Apr 04 '19 at 10:40
  • 1
    @GhostCat I was referring to the usual attempt to fill the array with zeros at the point where you know that you won’t use the array afterwards at all. That’s a scenario where it is not that unlikely that a JVM might detect that this is the object’s last use and hence, the write is obsolete. In principle, a JVM can eliminate *any* memory access, as long as it can ensure that the read is properly replaced by the value that would have been previously written, as indeed happens after Escape Analysis. But of course, if the password was never written into memory at all, we don’t have a problem. – Holger Apr 04 '19 at 10:54

1 Answers1

1

In Java one usually would use char[] array instead of String because this allows to manually zero the data in the array.

However even then the data might not be fully unset as per this answer:

As noted in the 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 garbage collector 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.

Similar problem exists in C/C++ if the compiler decides to optimize out memset. As per 11.4. Specially Protect Secrets (Passwords and Keys) in User Memory:

A Bugtraq post by Andy Polyakov (November 7, 2002) reported that the C/C++ compilers gcc version 3 or higher, SGI MIPSpro, and the Microsoft compilers eliminated simple inlined calls to memset intended to overwrite secrets. This is allowed by the C and C++ standards. Other C/C++ compilers (such as gcc less than version 3) preserved the inlined call to memset at all optimization levels, showing that the issue is compiler-specific. Simply declaring that the destination data is volatile doesn’t help on all compilers; both the MIPSpro and Microsoft compilers ignored simple "volatilization". Simply "touching" the first byte of the secret data doesn’t help either; he found that the MIPSpro and GCC>=3 cleverly nullify only the first byte and leave the rest intact (which is actually quite clever - the problem is that the compiler’s cleverness is interfering with our goals).

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111
  • Hey @Karol Dowbecki. Thanks for your answer. I'm wondering if using [pinned memory](https://stackoverflow.com/questions/1112909/how-can-i-pin-a-byte-array-in-java) such as with `ByteBuffer.allocateDirect` or directly using the object `Unsafe` would solve that case. – Jämes Apr 03 '19 at 20:27
  • In other words, no. – President James K. Polk Apr 03 '19 at 22:27
  • @ZenLulz That alone doesn't solve the problems but it does, a) avoid multiple copies in memory, b) avoid being visible in a heap dump or debug session, c) easily, but not completely reliably, overwritten. – Peter Lawrey Apr 09 '19 at 13:26