简短答案
我为什么不能说:
SecureString password = new SecureString("password");
因为现在你password
在记忆中;无法擦除它-这正是SecureString的要点。
长答案
SecureString存在的原因是因为完成后无法使用ZeroMemory擦除敏感数据。它的存在是为了解决由于 CLR 而存在的问题。
在常规的本机应用程序中,您将调用SecureZeroMemory
:
用零填充内存块。
注意:SecureZeroMemory与相同ZeroMemory
,只是编译器不会对其进行优化。
问题是您不能在.NET中调用ZeroMemory
或SecureZeroMemory
在其中调用。在.NET中,字符串是不可变的;您甚至无法像使用其他语言一样覆盖字符串的内容:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;
所以,你可以做什么?完成后,我们如何在.NET中提供清除内存中的密码或信用卡号的功能?
唯一可以完成的方法是将字符串放在某个本机内存块中,然后可以在其中调用ZeroMemory
。本机内存对象,例如:
- BSTR
- HGLOBAL
- CoTaskMem非托管内存
SecureString将丢失的功能恢复原状
在.NET中,完成后无法擦除字符串:
- 他们是一成不变的;你不能覆盖他们的内容
- 你不能
Dispose
人
- 他们的清理工作由垃圾收集者负责
SecureString是一种传递字符串安全性的方法,可以在需要时保证对其进行清理。
您问了一个问题:
我为什么不能说:
SecureString password = new SecureString("password");
因为现在你password
在记忆中;无法擦拭。它被卡在那儿,直到CLR决定重新使用该内存为止。您已经将我们放回了开始的位置;运行中的应用程序带有我们无法摆脱的密码,并且在内存转储(或进程监视器)可以看到密码的位置。
SecureString使用Data Protection API将加密的字符串存储在内存中;这样,该字符串将不会存在于swapfiles,崩溃转储中,甚至不会出现在本地变量窗口中,而同事会查看您的应有信息。
如何读取密码?
然后是问题:我如何与字符串互动?您绝对不想要这样的方法:
String connectionString = secureConnectionString.ToString()
因为现在您就回到了起点-无法删除的密码。您想强制开发人员正确处理敏感字符串-以便可以从内存中擦除它。
这就是.NET提供三个方便的帮助器功能以将SecureString编组到非托管内存中的原因:
您将字符串转换为非托管内存Blob,对其进行处理,然后再次擦除它。
一些API接受SecureStrings。例如在ADO.net 4.5中,SqlConnection.Credential接受一个SqlCredential集合:
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
您还可以在连接字符串中更改密码:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
.NET内部有很多地方,它们出于兼容性目的继续接受纯字符串,然后迅速将其放入SecureString中。
如何将文本放入SecureString?
仍然存在问题:
我如何首先将密码输入SecureString?
这是挑战,但是重点是让您考虑安全性。
有时已经为您提供了该功能。例如,WPF PasswordBox控件可以直接将输入的密码作为SecureString返回给您:
获取当前由PasswordBox保留的密码作为SecureString。
这很有用,因为您过去经常在各处传递原始字符串,现在您的类型系统抱怨SecureString与String不兼容。您需要花很长时间才能将SecureString转换回常规字符串。
转换SecureString很容易:
- SecureStringToBSTR
- PtrToStringBSTR
如:
private static string CreateString(SecureString secureString)
{
IntPtr intPtr = IntPtr.Zero;
if (secureString == null || secureString.Length == 0)
{
return string.Empty;
}
string result;
try
{
intPtr = Marshal.SecureStringToBSTR(secureString);
result = Marshal.PtrToStringBSTR(intPtr);
}
finally
{
if (intPtr != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(intPtr);
}
}
return result;
}
他们只是真的不希望您这样做。
但是,如何将字符串放入SecureString?那么,您需要做的就是首先停止在String中输入密码。您需要将其包含在其他内容中。甚至一个Char[]
数组也会有所帮助。
到那时,您可以附加每个字符并在完成后擦除纯文本:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
你需要存储在您的密码一些记忆,你可以擦拭。从那里将其加载到SecureString中。
tl; dr:SecureString存在以提供等效的ZeroMemory。
某些人看不到在锁定设备时从内存中清除用户密码,或在他们通过身份验证后从内存中清除键盘击键的意义。这些人不使用SecureString。
SecureString
进行新开发:github.com/dotnet/platform-compat/blob/master/docs/DE0001.md