.NET 优惠SecureString
为了这个表面上的目的。但它有一个巨大的警告,几乎破坏了它的实用性:
很少有 .NET API 支持它。这意味着您需要在之间进行转换SecureString
and 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
to a 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 缓存字符串的方式,这可能会产生比您预期更多的后果。除了从以下内容转换而来的纯文本字符串外,您永远不应该使用它SecureString
使用ToInsecureString
上面,因为您知道它们将存在于自己的缓冲区中。
这一切值得吗?老实说可能不是。试图将秘密保护在 RAM 中几乎总是一场失败的战斗。但如果您想尝试一下,或者只是需要勾选审核表上的复选框,那就去做吧。