12
$\begingroup$

In JP Aumasson's cryptocoding guidelines, he states that memory containing secret data should be cleared before it goes out of scope. This is to prevent vulnerabilities where, for example, an attacker could access the core dump file or use other memory exploits to recover sensitive data from uncleared memory. This is, of course, a problem for high-level programming languages that rely on garbage collection (such as python or java) since they make it very hard or even impossible to clear sensitive data from memory. So knowing this, does this mean that those languages should not be used for cryptographic applications?

New contributor
DerekKnowles is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$
1
  • 3
    $\begingroup$ The question that is linked seems to discuss how garbage collection can introduce timing side-channel attacks, which is different from the concern in this question that gc programming languages have the inability to clear sensitive data in memory $\endgroup$ Commented Nov 30 at 0:03

5 Answers 5

18
$\begingroup$

What you're talking about isn't an issue of garbage collection, but of "zeroizing" sensitive registers after use. See for example the Rust zeroize crate.

In an ideal world, one would do something like

let vec_of_secrets = Vec::new();
//
// Do secret stuff here
//
for i in vec_of_secrets.iter() {
    vec_of_secrets[i] = 0;
}
// de-allocate vec_of_secrets

The issue is that an optimizing compiler would be able to tell that assigning 0 to vec_of_secrets (which should "delete the secret values from memory") amounts to wasted clock cycles. This optimizing compiler may then elide performing this computation, and might leave the secret vector in memory. This isn't good.

As an easy way to see that this isn't about garbage collection, Rust is not a garbage-collected language, and still suffers this issue. Instead, it is about fighting an optimizing compiler when using secrets stored on the heap.

That being said, if such a programming language has a method to "zeroize" these secrets on the heap, there should be no issue. I don't personally know how easy this is to do in the example languages you mention.

$\endgroup$
7
  • $\begingroup$ I refer to garbage collection as JP Aumasson's cryptocoding guideline states: "Unfortunately, there's virtually no way to reliably clean secret data in garbage-collected languages (such as Go, Java, or JavaScript)" $\endgroup$ Commented Nov 30 at 3:52
  • 3
    $\begingroup$ @DerekKnowles That's not true for all garbage-collected languages. You can reliably clear secret data in C# via pinning. It's pretty crazy Go has this problem given its popularity for cryptography. Think the best you can do is something like memguard. $\endgroup$ Commented 2 days ago
  • $\begingroup$ If the language supports overriding constructors/destructors you can always put a zeroize routine in the destructors of any object-types you use. In that case the garbage collector should zeroize stuff as it does cleanup. $\endgroup$
    – user4574
    Commented yesterday
  • 2
    $\begingroup$ A note for JVM based languages: developers are (or were) encouraged to not use the String type for secure data such as passwords or keys due to the existence of string interning and the string pool. Instead, you use an array of characters that you zero out, then deallocate, similar to the Rust example. Although, in theory you can use the new operator (as it doesn't always intern), it is better to avoid the potential for a secret to leak via a String object altogether. Zeroing sensitive data as soon as you don't need it is a general rule for secure programming in all programming languages. $\endgroup$ Commented 18 hours ago
  • 1
    $\begingroup$ @LyndonArmitage It's not just string interning that's the issue. You can't clear the underlying array of a String (or shouldn't be able to). AFAIK, String interning only happens with hardcoded Strings and if you explicitly call intern(). $\endgroup$
    – JimmyJames
    Commented 17 hours ago
12
$\begingroup$

You can almost always zero the memory yourself after use. This may run into problems with optimizing compilers, like Mark says. Garbage collection will not release the memory before the last reference is out of scope, which would only happen after your zeroing operation.

There is a risk that garbage collection would relocate your object in RAM without clearing the old location (compaction). This is relatively low risk if your secret operation is not running for a long time. Some garbage collectors may have a way to disable compaction or do not compact at all. This increases heap fragmentation, but not more than in non-GC languages.

Garbage collection is not the only mechanism that can cause secret data to leak. Especially system swap is risky, as many high level programming languages do not have a way to lock memory from swapping, and the data could persist in the swapfile for a long time. System level configuration can encrypt the swap or force zeroing of it.

Overall, zeroing memory after use is a defence-in-depth strategy. The attacker shouldn't be able to read your memory in the first place, but if they do, zeroing reduces the risk they'll find anything important. The risk will always exist, as the data will be in memory during the processing.

So as a summary:

  • Zeroing is useful in GC languages, though slightly less effective as in non-GC languages.
  • Compiler optimizations can accidentally remove zeroing instructions.
  • Zeroing is about reducing the risk of finding secret data in RAM, but the risk can never be fully eliminated.
$\endgroup$
1
  • $\begingroup$ Not all GC is compacting. Copy collection is often used and can be extremely frequent depending on numerous factors. In Java this is often called a minor collection. I've seen real-world applications that run minor collections on the order of every few milliseconds. This is generally a result of a poor configuration, but it happens. $\endgroup$
    – JimmyJames
    Commented 17 hours ago
7
$\begingroup$

A language using GC is a red herring. What is desirable is control of critical memory but not necessarily allocation/deallocation. This is not at odds with GC.

Since you mention Python, there are two important features of GC languages to point out with practical examples.

At the end of the day, no matter what kind of language is used it is important to be aware of how sensitive data is handled by the language in practice. Security considerations go well beyond the simple, default memory management semantics of mainstream languages and need explicit handling in either memory model.

New contributor
MisterMiyagi is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$
4
  • 3
    $\begingroup$ Indeed, even in a non-GC language, it would be possible that a memory page holding sensitive data gets swapped out to an SSD and written into part of a block that contains enough useful that it survives a long time without ever being physically erased (flash memory is subdivided into 256K or larger blocks that can only be erased as a unit, so if a block has 240K of useful data and 4K of garbage, an SSD would need to copy the 240K of data somewhere else before it could erase the garbage). $\endgroup$
    – supercat
    Commented 2 days ago
  • 3
    $\begingroup$ There's an additional issue with compacting GCs not found in other runtimes, though: namely the fact that the GC may copy the secret during the compaction phase, so that even zeroing the secret after use leaves the secret openly displayed in its original location. As mentioned by @memguard in a comment, some languages have pinning primitives to prevent this copy. $\endgroup$ Commented yesterday
  • $\begingroup$ @supercat paging/swapping stuff can be encrypted. Like encryptpagingfile on Windows. $\endgroup$
    – tevemadar
    Commented yesterday
  • $\begingroup$ The risks are essentially the same as with GC languages: code would be reliant upon forces outside its control to maintain information security. $\endgroup$
    – supercat
    Commented yesterday
3
$\begingroup$

It's not exactly the GC, but the string. In languages where strings are immutable you can't overwrite them either. So writing password="" or password="notapassword" in Python, Java, and many others will leave previous content in the memory allocated, which then will get collected by the GC, but even then the actual bytes will remain being intact (GC does not zero out memory it reclaims).

Sometimes people try things, like the built-in Console class in Java gives you an array of characters from readPassword(), so you can overwrite the contents after use:

public char[] readPassword()

And then many password hashing functions really support character arrays.

GUI stuff in Java varies, like AWT has its generic text input support an echo character, gives you a String, Swing can get you both String and character array (a question is what it does/stores internally), and then JavaFX is again a text input with minimal changes.

It also matters what you're worried about exactly. By the time high-level language code gets a password, it has been to quite many places, starting with the device drivers that may have buffers internally and may not know you're reading a password at the moment, especially since one Alt-Tab ago keypresses went to a totally different application.

New contributor
tevemadar is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
$\endgroup$
2
$\begingroup$

Overwriting memory is hard in any language.

The memory contents are typically not part of the contract of language constructs and functions. E.g. printf specifies it shows something on the console, but not whether that is also present in memory or not. The memory contents are not considered externally observable behavior, and programming languages often do not provide control over memory contents.

This is, of course, a problem for high-level programming languages that rely on garbage collection (such as python or java) since they make it very hard or even impossible to clear sensitive data from memory.

Back in 2016 I looked into whether it is possible to clear secrets from memory in Python, and came to the conclusion this is almost impossible.

In Java, a common pattern is to store secrets in a char array instead of in a String. This makes it possible to overwrite the char array, and thus clear the secret from memory.

There is an assumption in there, that the char array in Java is mapped one-to-one to memory. I am not sure this is specified somewhere, or an implementation detail. Perhaps a compliant JVM could exist that keeps the secret in memory after overwriting the char array.

$\endgroup$
1
  • $\begingroup$ "Perhaps a compliant JVM could exist that keeps the secret in memory after overwriting the char array." As I noted on another answer, the most likely scenario where a character array is 'orphaned' with sensitive data is in a copy collector. That is you could have an array with sensitive data be copied from one 'side' to the other prior to zeroing out. The original copy would remain untouched until its space is reused. $\endgroup$
    – JimmyJames
    Commented 17 hours ago

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.