我什么时候需要.NET中的SecureString?
我正在尝试.NET的SecureString的目的。 来自MSDN:
System.String类的一个实例是不可变的,当不再需要时,不能以编程方式调度垃圾收集; 也就是说,实例在创build后是只读的,并且不可能预测实例何时从计算机内存中删除。 因此,如果String对象包含密码,信用卡号码或个人数据等敏感信息,则在使用该信息之后可能会泄露信息,因为您的应用程序无法从计算机内存中删除数据。
一个SecureString对象类似于一个String对象,因为它有一个文本值。 但是,SecureString对象的值会自动encryption,可以修改,直到应用程序将其标记为只读,并且可以由应用程序或.NET Framework垃圾收集器从计算机内存中删除。
SecureString实例的值在实例初始化或值被修改时自动encryption。 您的应用程序可以通过调用MakeReadOnly方法呈现实例不可变,并防止进一步修改。
自动encryption是巨大的回报?
为什么我不能说:
SecureString password = new SecureString("password");
代替
SecureString pass = new SecureString(); foreach (char c in "password".ToCharArray()) pass.AppendChar(c);
我缺less什么SecureString的方面?
当前使用SecureString
的框架的一些部分:
- WPF的
System.Windows.Controls.PasswordBox
控件将密码保存为内部的SecureString(通过PasswordBox::SecurePassword
作为副本公开) -
System.Diagnostics.ProcessStartInfo::Password
属性是一个SecureString
-
X509Certificate2
的构造函数使用SecureString
作为密码
主要目的是减less攻击面,而不是消除它。 SecureStrings
被“固定”在RAM中,所以垃圾收集器不会移动它或复制它。 它还确保明文不会写入交换文件或核心转储。 encryption更像混淆,不会阻止黑客,但是谁能够find用于encryption和解密的对称密钥。
正如其他人所说的,你必须逐个创build一个SecureString
的原因是因为第一个明显的缺陷,否则你可能已经有一个秘密值作为一个普通的string了,所以有什么意义呢?
SecureString
是解决Chicken-and-egg问题的第一步,所以即使大多数当前的场景需要将它们转换回常规string来使用它们,现在它们在框架中的存在意味着更好地支持它们未来 – 至less到你的程序不必成为薄弱环节的地步。
很多很棒的答案; 这里简要介绍一下已经讨论过的内容。
微软已经实施了SecureString类,以提高敏感信息(如信用卡,密码等)的安全性。 它会自动提供:
- encryption(在内存转储或页面caching的情况下)
- 固定在内存中
- 标记为只读的能力(以防止进一步的修改)
- 通过不允许传递一个常量string来保证安全的构造
目前,SecureString在使用上受到限制,但预计将来会有更好的采用。
根据这些信息,SecureString的构造函数不应该只是取一个string并将其分割为char数组,因为使用拼写的string会破坏SecureString的目的。
附加信息:
- .NET安全博客的一篇文章谈论的内容与此处所描述的大致相同。
- 另一个重新访问它,并提到一个可以转储SecureString的内容的工具。
编辑:我发现很难select最好的答案,因为有很多很好的信息; 太糟糕了,没有辅助答案选项。
有很less的情况下,你可以在当前版本的框架中明智地使用SecureString。 这实际上仅用于与非托pipeAPI交互 – 您可以使用Marshal.SecureStringToGlobalAllocUnicode对其进行编组。
只要你把它转换成/从一个System.String,你已经击败了它的目的。
MSDN 示例从控制台input中每次生成一个字符SecureString,并将安全string传递给非托pipeAPI。 这是相当复杂和不切实际的。
您可能会预期.NET的未来版本将更多地支持SecureString,从而使其更有用,例如:
-
SecureString Console.ReadLineSecure()或类似于将控制台input读取到SecureString中,但不包含示例中的所有复杂代码。
-
WinForms文本框replace将其TextBox.Text属性存储为安全string,以便可以安全地input密码。
-
扩展到安全相关的API以允许密码以SecureStringforms传递。
没有上述,SecureString将是有限的价值。
我相信你必须做字符附加而不是一个平面实例的原因是因为在后台传递“密码”给SecureString的构造函数将“密码”string放在内存中,以防止安全string的目的。
通过附加你只是把一个字符在一个时间内,这是不相邻的物理,使得重build原来的string更难。 我可能在这里是错的,但这是如何解释给我的。
该类的目的是防止通过内存转储或类似工具暴露安全数据。
MS发现,在某些导致服务器(桌面,无论)崩溃的情况下,运行时环境会执行内存转储,暴露内存中的内容。 安全string将其encryption到内存中,以防止攻击者能够检索string的内容。
简答
为什么我不能只说:
SecureString password = new SecureString("password");
因为现在你在内存中有password
; 没办法擦拭它 – 这正是SecureString的要点。
长答案
SecureString存在的原因是因为当您完成它时,您不能使用ZeroMemory擦除敏感数据。 它是为了解决由于 CLR而存在的问题。
在常规的本地应用程序中,您可以调用SecureZeroMemory
:
用零填充一块内存。
注意 : 除了编译器不会优化它 ,SecureZeroMemory与ZeroMemory
完全相同。
问题是你不能在.NET中调用ZeroMemory
或SecureZeroMemory
。 在.NET中,string是不可变的。 你甚至不能像在其他语言中那样覆盖string的内容:
//Wipe out the password for (int i=0; i<password.Length; i++) password[i] = \0;
所以,你可以做什么? 我们如何提供.NET的能力,当我们完成它时,从内存中擦除密码或信用卡号码?
唯一可行的方法是将string放在某个本地内存块中,然后可以调用ZeroMemory
。 本机内存对象,如:
- 一个BSTR
- 一个HGLOBAL
- CoTaskMem非托pipe内存
SecureString给予失去的能力
在.NET中,当您完成string时,不能清除string:
- 他们是不可改变的; 你不能覆盖他们的内容
- 你不能
Dispose
他们 - 他们的清理是在垃圾收集器的摆布
SecureString作为一种传递string安全的方式存在,并且能够在需要时保证清理。
你问了这个问题:
为什么我不能只说:
SecureString password = new SecureString("password");
因为现在你在内存中有password
; 没办法擦拭它。 直到CLR决定重新使用这个内存时,它一直停留在那里。 你已经把我们放回到我们开始的地方。 一个正在运行的应用程序和我们无法摆脱的密码,以及内存转储(或进程监视器)可以看到密码的位置。
SecureString使用Data Protection API来存储在内存中encryption的string; 这样,string将不会存在于swapfiles,崩溃转储,甚至在本地variables窗口与同事看你的应该。
我如何阅读密码?
那么问题是:我如何与string交互? 你绝对不希望有如下的方法:
String connectionString = secureConnectionString.ToString()
因为现在你回到了你开始的地方 – 你无法摆脱的密码。 您希望强制开发人员正确处理敏感string – 以便从内存中擦除。
这就是为什么.NET提供了三个方便的帮助函数来将SecureString编组到非托pipe内存中:
- SecureStringToBSTR (用ZeroFreeCoTaskMemUnicode释放)
- SecureStringToCoTaskMemUnicode (用ZeroFreeCoTaskMemUnicode释放)
- SecureStringToGlobalAllocUnicode (使用ZeroFreeGlobalAllocUnicode释放)
您将string转换为非托pipe内存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();
您也可以在连接string中更改密码:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
而且在.NET内部有很多地方,为了兼容性的目的,它们继续接受一个普通的string,然后快速地将其转换成一个SecureString。
如何把文本放入SecureString?
这仍然留下了问题:
我如何获得密码到SecureString的第一个地方?
这是挑战,但重要的是让你思考安全问题。
有时function已经为您提供。 例如,WPF PasswordBox控件可以直接将您input的密码作为SecureString返回给您:
PasswordBox.SecurePassword属性
获取PasswordBox当前保存的SecureString的密码 。
这是有帮助的,因为你曾经绕过一个原始string的地方,你现在有types系统抱怨说SecureString与String不兼容。 在将SecureString转换回常规string之前,您希望尽可能长时间。
转换一个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; }
他们真的不希望你这样做。
但是,我如何获得一个string到SecureString? 那么你需要做的就是停止在第一个string中的密码。 你需要有其他东西。 即使是一个Char[]
数组也是有帮助的。
这就是当你完成后可以附加每个字符并擦除明文的时候:
for (int i=0; i < PasswordArray.Length; i++) { password.AppendChar(PasswordArray[i]); PasswordArray[i] = (Char)0; }
你需要把你的密码保存在你可以擦除的内存中。 从那里加载到SecureString。
tl; dr: SecureString存在提供相当于ZeroMemory 。
有些人看不到在设备被locking时从内存中擦除用户密码的问题 ,或者在authentication之后擦除内存中的按键 。 那些人不使用SecureString。
那么,正如描述所述,这个值被encryption存储,意味着你的进程的内存转储将不会显示string的值(没有一些相当严重的工作)。
你不能只是从一个常量string构造一个SecureString的原因是因为你将有一个未encryption的string在内存中的版本。 限制你创build分段的string可以降低一次在内存中存储整个string的风险。
SecureString最大的好处之一就是避免了由于页面caching而导致数据存储到磁盘的可能性。 如果您在内存中有密码,然后加载大型程序或数据集,则当您的程序被分页出内存时,可能会将密码写入交换文件。 使用SecureString,至less数据不会以明文forms无限地坐在磁盘上。
我想这是因为string是安全的,即黑客不应该能够读取它。 如果用string初始化它,黑客可以读取原始string。
我会停止使用SecureString。 看起来PG小子正在放弃对它的支持。 可能甚至在将来拉它 – https://github.com/dotnet/apireviews/tree/master/2015-07-14-surestring 。
我们应该在.NET Core中的所有平台上从SecureString中删除encryption – 我们应该过时SecureString – 我们可能不应该在.NET Core中公开SecureString
另一个用例是当您使用付款应用程序(POS)时,由于您是小心谨慎的开发人员,因此您无法使用不可变的数据结构来存储敏感数据。 例如:如果我将敏感的卡数据或授权元数据存储到不可变的string中,那么在数据被丢弃之后,这些数据在内存中可用的时间总是很长。 我不能简单地覆盖它。 这种敏感数据在内存中保存的另一个巨大优势就是encryption。