我有一个c#应用程序,它用敏感数据初始化一些变量。它们不是密码,但我们认为它们很敏感。我想保护的变量值都是字符串类型的
我想做的是找到一种机制(我知道不是100%的机制),允许我保护内存中的变量值,或者至少让攻击者更难读取它们。因此,我可以保护应用程序内存中的敏感数据,使其更"不可读"。任何其他可以在运行时跟踪、转储或读取我的应用程序的应用程序。
我看到存在微软数据保护API (DPAPI),它有一个叫做ProtectedMemory的类,它提供了两个方法保护和不保护,这里是一个例子。
不管怎样,看起来微软已经停止使用ProtectedMemory了,就像有人说的那样,因为它没有用。
这里的其他人建议用一个好的函数(例如SHA1/2)来散列它们并存储摘要。
我想使用私有属性(因为它只在我的类中使用)而不是一个简单的变量,它的set属性将其加密到内存中,而get属性将其解密。如下:
public static MyClass
{
private string MyVariable
{
get
{
return Encoding.UTF8.GetString(ProtectedMemory.Unprotect(_myVariable, MemoryProtectionScope.SameLogon));
}
set
{
_myVariable = Encoding.UTF8.GetBytes(value);
ProtectedMemory.Protect(_myVariable, MemoryProtectionScope.SameLogon);
}
}
private byte[] _myVariable;
}
其余的字符串变量也是一样。
我想在他的帖子中做同样的事情,但是有些人说将受保护的内存从私有支持字段移动到属性不会有帮助(我不确定他指的是什么,如果使用属性不受保护¿?)。你还能确认我是否使用私有属性为我的变量将在内存中受到保护吗?
我该怎么做呢?有人能提供一个简单的代码片段吗?加密/解密过程应该相对较快。
我不想使用任何第三方,只有操作系统的帮助和。net框架。
。NET为这个表面上的目的提供了SecureString
。但它附带了一个巨大的警告,几乎破坏了它的效用:
很少。net api支持它。这意味着您需要在应用程序的生命周期中多次在SecureString
和string
之间进行转换。每次这样做都有机会将秘密值泄漏到未加密的RAM中。因此,当使用unsafe
代码不再需要字符串的明文副本时,您必须非常小心地擦除它们。另外,当将string
传递给其他api时,您不知道将创建多少个秘密副本。以JSON序列化为例-如果您需要将SecureString
的未加密值放在JSON流中,那么在JSON序列化完成后,该值很容易存在于RAM中的其他地方,可能会多次存在。HTTP报头和其他通常交换秘密的地方也是如此。
说了这么多,如果你想冒险一试,你需要知道如何从SecureString
转换为正常的string
,因为99.99%的。net api不支持它:
public static string ToInsecureString(this SecureString str)
{
if (str == null)
return null;
if (str.Length == 0)
return "";
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = SecureStringMarshal.SecureStringToGlobalAllocUnicode(str);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
以及如何将普通string
转换为SecureString
:
public static SecureString ToSecureString(this string s, bool readOnly = true)
{
if (string.IsNullOrEmpty(s))
return null;
unsafe
{
fixed (char* chars = s)
{
var ss = new SecureString(chars, s.Length);
if (readOnly)
ss.MakeReadOnly();
return ss;
}
}
}
最后,您需要一种方法将明文string
的字节变为零,当您完成它时:
public static void ScrubString(ref string s)
{
if (s == null)
return;
unsafe
{
fixed (char* chars = s)
{
for (int i = 0; i < s.Length; i++)
{
chars[i] = (char)0;
}
}
}
}
你必须非常小心最后一个;由于。net缓存字符串的方式,这可能会产生比您预期的更多的后果。除了使用上面的ToInsecureString
从SecureString
转换成的明文字符串之外,你不应该使用这个,因为你知道这些字符串将存在于它们自己的缓冲区中。
这一切值得吗?老实说,可能不会。试图在ram中保护秘密几乎总是一场失败的战斗。但如果你想尝试一下,或者只是需要在审计表单上勾选一个复选框,那就去做吧。