Securing a symmetric encryption key in memory

Viewed 1123

I have an application where I am retrieving the symmetric encryption key present in the disk and using it to encrypt data. The encryption key is retrieved from the disk when the program starts and is stored as an array of bytes in a private class variable. Right after the key is retrieved from the disk on the start of the program, ProtectedMemory.Protect() is used on the key to protect it. The key is unprotected by ProtectedMemory.Unprotect() every time it needs to be used and is again protected after use.

The parts which has got me pondering on the effectiveness of this scheme is during the instances where the key is retrieved from the disk and every time the key needs to be used as an easily exploitable vulnerability is created during 2 key moments in the program's execution cycle: when the program has just finished loading the key from the disk and has not called the Protect() method and when the key is unprotected for use during encryption.

class ApplicationClass {
   private byte[] encKey;

   public ApplicationClass() {
     // Fetches the encryption key first
     encKey = StorageInt.FetchKey(); // Fetches and returns the encrypted key from the disk

     // A gaping vulnerability here as the key is just loaded in memory and is not protected
     ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);

     // Other initialization instructions follows
   }

   private byte[] ApplySymmEnc(byte[] plaintext) {
     Aes aes = Aes.Create();
     byte[] iv = new byte[128];
     RNGCryptoServiceProvider randomBytesGenerator = new RNGCryptoServiceProvider();
     randomBytesGenerator.GetNonZeroBytes(iv);
     randomBytesGenerator.Dispose();
     ProtectedMemory.Unprotect(encKey, MemoryProtectionScope.SameProcess);

     // Another gaping vulnerability here!

     ICryptoTransform encryptor = aes.CreateEncryptor(encKey, iv);
     ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess); // Protect the key right after it is used for encryption

     // Instructions for encryption follows
   }
}

Thanks in advance.

EDIT: As for the reason on being unconcerned about the key's security while on the disk, the key exists in a reasonably secure obfuscated form in the disk which is deciphered by the StorageInt.FetchKey() function while retrieving.

3 Answers

Protecting against all attacks is impossible. The closest you could get is probably using a Trusted Platform Module (tpm) chip so the key never leaves the chip. Second best may be to use a trusted execution environment if such is offered by your processor. But neither is impervious to all kinds of attacks.

If dedicated hardware support is not available it is probably not feasible to try to protect your program from an administrator. If the attacker can read the memory of your program, why could he not just read the key from disk?

Encrypting the key in ram is useful to protect against things like cold-boot attacks, and such attacks would probably be difficult to time right to get the key at a vulnerable moment.

There are also some answers in the question best practices for keys in memory that might be useful.

Keeping symmetric encryption keys protected during those times (after fetching from disk but before calling ProtectedMemory.Protect) would need OS level support, which doesn't exist in Windows (and other .net core platforms).

Directly from the .net team on a similar topic dealing with credentials: "The purpose of SecureString is to avoid having secrets stored in the process memory as plain text. ... However, even on Windows, SecureString doesn't exist as an OS concept ... It just makes the window getting the plain text shorter ... The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication" (https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md)

You have mentioned

"when the program has just finished loading the key from the disk and has not called the Protect()"

If I rephrase the above sentence StorageInt.FetchKey() is fetching you an unprotected key and you want to protect it at this point.

You could create an extension method for StorageInt.FetchKey(bool IsProtectKey = true) this method can call ProtectedMemory.Protect(). You can use only the extension method to fetch key.

public int StorageInt.FetchKey(bool IsProtectKey = true)
{
    encKey = StorageInt.FetchKey();
    CodeEncryptedKey = EncryptAndStoreProtectedKey(); 
    //Above function should encrypt your "encKey" to AES256 or to any secure encryption algorithm, store it in cache and return encrypted key
    
    ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);            
}
        
Public string UseKey(string CodeEncryptedKey)
{
    encKey = GetProtectedKeyFromCache(CodeEncryptedKey)
    ProtectedMemory.Unprotect(encKey, MemoryProtectionScope.SameProcess);
    Task.Run(() => ProtectKeyAfter5Seconds(encKey));
    return encKey;
}
        
Void ProtectKeyAfter5Seconds(encKey)
{
    Thread.Sleep(5000); //I'm telling here to encrypt after 5 seconds, you can have your logic to encrypt after one-time use or any particular logic
    ProtectedMemory.Protect(encKey, MemoryProtectionScope.SameProcess);
}

Does this help?

Related