19
$\begingroup$

When dealing with cryptographic data, is there a chance that attacker recovers portion of data I used in my program? Should I try to delete every bit of keys and encrypted data that I used, overwrite it with zeroes?

And one extra, how is situation different in languages with garbage collection like Python or Java?

$\endgroup$
1
  • 3
    $\begingroup$ What are you trying to prevent exactly? Presumably an "attacker" who had access to the machine's memory would thereby be entitled to the data the machine had processed. And if your model is that an attacker may have control over the machine while it's processing the data, erasing it later won't help. Are you trying to protect against sensitive data in swap or core dumps? $\endgroup$ Commented Aug 28, 2013 at 7:04

4 Answers 4

18
$\begingroup$

The book Cryptography Engineering devotes part of a chapter to this topic. Overwriting sensitive data with zeroes is a good start, but there are lots of other considerations.

  • If you rely on a language's default object destruction behavior to zero the memory, it's possible for an unexpected error to prematurely halt the program's execution without it cleaning up after itself. In Java, exception handling can discard object references without cleaning up the objects themselves. [Not sure if this is still the case]

  • Sometimes compilers will optimize away the code used to overwrite the memory because it's acting on a piece of memory with no other uses in the program. There are a few ways to trick the compiler into not performing this "dead store elimination".

  • OS memory management can foil your attempts to delete data by paging it to the system's hard disk. There are ways of preventing this but they are language- and platform-specific.

  • If the OS does a context switch the values in the registers are stored elsewhere in memory until execution resumes. This can cause sensitive data to end up in strange places. Modern processors also have multiple caches where sensitive data can be held.

  • In languages where memory management is automatic it's even more difficult to know where exactly in the system your allocated memory is (and for how long). In Java, objects all live on the heap and are reference-counted as a part of garbage collection. When there are no more references to the object, it can be deleted by the next pass of the garbage collector. However, short of calling System.gc() there's no way of knowing when garbage collection will occur. And even if you force garbage collection it still might not execute immediately. This SO question talks a little bit about System.gc behavior.

  • Cold boot attacks happen when keys are always written to the same physical region of a computer's memory. Over time, the electromagnetic forces can cause the memory to retain a state after the power is shut off.

This probably isn't all in scope for your application, but it should give you a good idea of the capabilities of an attacker with physical system access. The most important thing you can do is know your language and platform very, very well. For further reading, this Wikipedia article should give you a good start.

$\endgroup$
3
  • 1
    $\begingroup$ Number 2 is particularly important. For instance you can look at how OpenSSL does it, by doing strange operations on the data to overwrite to trick the compiler into randomizing it even though it will never be used later on (for higher level languages this may well be impossible to achieve). There is indeed no generic solution. $\endgroup$
    – Thomas
    Commented Aug 27, 2013 at 3:36
  • $\begingroup$ Excellent answer, although technically, it answers the question "how do I erase sensitive data I used in my programmed" and not the question "should I erase sensitive data I used in my program". $\endgroup$ Commented Aug 27, 2013 at 13:59
  • $\begingroup$ Haha I guess it does. I thought I'd kill two birds with one stone. $\endgroup$
    – pg1989
    Commented Aug 27, 2013 at 17:15
4
$\begingroup$

Yes, it's a good idea. But unfortunatelly it's far from trivial to securely wipe data from memory. Modern compilers, operating system and CPUs make it really, really hard.

For instance you never know where your computer has stored sensitive data. CPUs have L1, L2 and shared L3 caches. NUMA (even ccNUMA) can do fancy stuff with your data until you enforce a memory barrier. The operating system may decide to swap the memory pages to disk, too. mlock() could help but it requires extra capabilities and is hard to use, because it operates on pages.

A simple call to memset() is wrong most of the time as well. Every state-of-the-art compiler will silently remove memset()-based cleanup code. C11 has introduced memset_s() but it's not yet available in most CRTs. See https://www.securecoding.cert.org/confluence/display/seccode/MSC06-C.+Be+aware+of+compiler+optimization+when+dealing+with+sensitive+data

You can try code like

static void * (* const volatile __memset_vp)(void *, int, size_t) = (memset);
(*__memset_vp)(s, c, n);

or

memset(s, c, n);
asm volatile("" : : "r"(s) : "memory"); /* may need GCC */

or

SecureZeroMemory(s, n); /* Windows only */

too.

For CPython you have to implement your own data type in C and wipe memory in its deallocation function. Python's core types won't get a flag for secure wiping anytime soon (most like never).

$\endgroup$
3
  • $\begingroup$ Your __memset_vp code is wrong. A compiler can always compare the __memset_vp function pointer and if it is equal to memset omit the code. $\endgroup$ Commented Dec 21, 2014 at 20:07
  • $\begingroup$ Your memset code that uses the inline asm to explain to GCC that the data may be read later is better but does not take into account multithreading. For example, your zeroing writes could be stuck in a core's store buffer for a bit before the memory is actually zeroed. You should also flush OS and hardware caches. $\endgroup$ Commented Dec 21, 2014 at 20:09
  • 1
    $\begingroup$ @StevenStewart-Gallus Yes, comparing the __memset_vp function pointer is a legal optimization but that does not make the code "wrong". Keep in mind that there's no portable way of doing this. If you don't have asm etc, the volatile fp approach works in practice. No compiler is doing this optimization so far. $\endgroup$ Commented Apr 28, 2020 at 8:37
4
$\begingroup$

This question is actually not entirely easy to answer. Usually, adding complexity to a cryptographic scheme or implementation, should be avoided, unless the added complexity is necessary in order to meet a specific requirement. The problem with software that is zeroing internal memory, is just that it is hard to come up with a credible scenario, where this is both effective and necessary, in order to counter a specific threat.

Suppose you have a client certificate in a password protected .pfx file on disk. You trust that the passphrase you use for the .pfx file is strong enough, for the encryption not to be broken, at least before the certificate expires. You use the certificate in a program you know implements state-of-the-art methods for zeroing internal memory. Later, either you discover that you have had malware on the same computer, or the computer is physically stolen. Would you, in either case, trust the methods that are zeroing internal memory, or would you revoke your certificate? Arguably, the sane thing to do, would be to revoke the certificate, regardless of whether the software is zeroing internal memory or not.

$\endgroup$
2
$\begingroup$

When dealing with cryptographic data, is there a chance that attacker recovers portion of data I used in my program?

Yes. See that other answer.

Should I try to delete every bit of keys and encrypted data that I used, overwrite it with zeroes?

It depends on goal:

  1. It is demonstrably useful to get higher marks in at least some security reviews/evaluations. Thus definitely yes, if that's the objective¹.
  2. It is easy to construct artificial cases where that does block an attack², thus there is some rationale to the practice and what said reviews/evaluations do. Thus rather yes, if there is no evidence of much more serious issues to be fixed.
  3. It is a lot trouble³ to perform reliably and portably, makes the code less readable, slightly slower and larger, helps automatic identification of code manipulating sensitive data, all this for a protection that with significant likelihood can be circumvented by a slightly different attack. Thus no, it's often not worth the effort absent 1, or before a lot of serious work in other areas important for security has been performed (prevention of buffer overflows, timing attacks, other side-channels..)

In summary: zeroization? Maybe, but don't count on it to make an insecure system secure. The only case where zeroization is technically a must is in code handling a suspected attack, destroying all sensitive material such as long-term key.


¹ Or if quickly and effortlessly adding many plausibly useful lines to the code is an objective.

² Typically an attack relying on a coding mistake, including some really made in deployed systems.

³ It is needed a level of control plain unavailable in many modern languages, especially interpreted. And for compiled languages, it is needed a special semantic known by the compiler, or outsmarting the optimizer (with the risk of being over-outsmarted by the next release or a different compiler), or disabling optimization (which can cost significant performance, and be reverted inadvertently, especially during a port).

$\endgroup$

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.