我有一个应用程序,在其中检索磁盘中存在的对称加密密钥并使用它来加密数据。程序启动时从磁盘检索加密密钥,并将其作为字节数组存储在私有类变量中。在程序启动时从磁盘中检索到密钥之后,立即在密钥上使用ProtectedMemory.Protect()
对其进行保护。每次需要使用ProtectedMemory.Unprotect()
时,密钥均不受保护,使用后再次受到保护。
使我思考该方案有效性的部分是在从磁盘检索密钥的情况下,并且每次需要在程序的两个关键时刻创建密钥以用作易于利用的漏洞的情况下,执行周期:程序刚刚从磁盘完成密钥加载,并且尚未调用Protect()
方法,并且密钥在加密过程中未受保护时使用。
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
}
}
提前感谢。
编辑:由于不关心密钥在磁盘上的安全性的原因,密钥在磁盘中以相当安全的混淆形式存在,而该形式在检索时由StorageInt.FetchKey()
函数解密。
防范all攻击是不可能的。您能获得的最接近的大概是使用Trusted Platform Module(tpm)芯片,因此密钥永远不会离开芯片。第二好的可能是使用trusted execution environment(如果您的处理器提供了此功能)。但是,这两种方法都不能阻止所有类型的攻击。
如果没有专用的硬件支持,尝试保护程序免受管理员的攻击可能是不可行的。如果攻击者可以读取程序的内存,为什么他不能仅从磁盘读取密钥?
加密ram中的密钥对于防止诸如冷启动攻击之类的攻击非常有用,并且可能很难及时在脆弱的时刻获取此类密钥。
问题best practices for keys in memory中也有一些可能有用的答案。
保持对称加密密钥在那段时间(从磁盘中获取之后,但在调用ProtectedMemory.Protect之前)将需要操作系统级别的支持,而Windows(和其他.net核心平台中不存在)。
直接来自.net团队,涉及与凭证有关的类似主题:“ SecureString的目的是避免将秘密信息以纯文本格式存储在进程内存中。...但是,即使在Windows上,SecureString也不存在。一个操作系统概念... [[只是使窗口中的纯文本短一些]] ... ...处理凭据的一般方法是避免使用凭据,而是依靠其他方式进行身份验证,例如证书或Windows身份验证。 (https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md)
“,当程序刚从磁盘加载完密钥后,尚未调用Protect()
“
如果我改写以上句子StorageInt.FetchKey()
,则是为您获取了一个不受保护的密钥,并且您现在想保护它。
您可以为StorageInt.FetchKey(bool IsProtectKey = true)
创建扩展方法,该方法可以调用ProtectedMemory.Protect()
。您只能使用扩展方法来获取密钥。
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); //Im 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);
}
有帮助吗?