我试图了解.NET的SecureString的目的.来自MSDN:
System.String类的一个实例都是不可变的,当不再需要时,不能以编程方式安排垃圾收集; 也就是说,实例在创建后是只读的,并且无法预测实例何时从计算机内存中删除.因此,如果String对象包含敏感信息(如密码,信用卡号或个人数据),则使用该信息后可能会显示该信息,因为您的应用程序无法从计算机内存中删除该数据.
SecureString对象类似于String对象,因为它具有文本值.但是,SecureString对象的值会自动加密,可以修改,直到您的应用程序将其标记为只读,并且可以通过应用程序或.NET Framework垃圾收集器从计算机内存中删除.
初始化实例或修改值时,SecureString实例的值会自动加密.您的应用程序可以呈现实例不可变,并通过调用MakeReadOnly方法防止进一步修改.
自动加密是最大的收益吗?
为什么我不能说:
SecureString password = new SecureString("password");
代替
SecureString pass = new SecureString(); foreach (char c in "password".ToCharArray()) pass.AppendChar(c);
我错过了SecureString的哪个方面?
目前使用的框架的某些部分SecureString
:
WPF的System.Windows.Controls.PasswordBox
控件在内部将密码保存为SecureString(作为副本公开PasswordBox::SecurePassword
)
该System.Diagnostics.ProcessStartInfo::Password
物业是一个SecureString
用于X509Certificate2
获取SecureString
密码的构造函数
主要目的是减少攻击面,而不是消除它.SecureStrings
在RAM中被"固定",因此垃圾收集器不会移动它或复制它.它还确保纯文本不会写入Swap文件或核心转储.加密更像是混淆,并且不会阻止确定的黑客,谁能够找到用于加密和解密它的对称密钥.
正如其他人所说的那样,你必须创建一个SecureString
逐个字符的原因是因为第一个明显的缺点:你可能已经将秘密值作为一个普通的字符串了,那么重点是什么?
SecureString
s是解决Chicken-and-Egg问题的第一步,因此即使大多数当前场景需要将它们转换回常规字符串以完全使用它们,它们在框架中的存在现在意味着更好地支持它们.未来 - 至少到你的程序不一定是薄弱环节的地步.
很多很棒的答案; 这是对所讨论内容的快速概述.
Microsoft已实施SecureString类,以便为敏感信息(如信用卡,密码等)提供更好的安全性.它自动提供:
加密(在内存转储或页面缓存的情况下)
固定在记忆中
标记为只读的能力(以防止任何进一步的修改)
通过不允许传入常量字符串的安全构造
目前,SecureString的使用受到限制,但预计将来会更好地采用.
根据这些信息,SecureString的构造函数不应该只是取一个字符串并将其切片到char数组,因为拼写的字符串会破坏SecureString的目的.
附加信息:
一个岗位由.NET安全作为这里所涉及官方博客谈论大同小异.
而另一个 重新审视,并提一个工具,可以转储SecureString的内容.
编辑:我发现选择最佳答案很难,因为很多信息很好; 太糟糕了,没有辅助的答案选项.
为什么我不能说:
SecureString password = new SecureString("password");
因为现在你有password
记忆; 无法擦除它 - 这正是SecureString的要点.
SecureString存在的原因是因为当您完成它时,您无法使用ZeroMemory擦除敏感数据.它的存在是为了解决因 CLR 而存在的问题.
在常规本机应用程序中,您可以调用SecureZeroMemory
:
用零填充内存块.
注意:SecureZeroMemory与之相同ZeroMemory
,但编译器不会对其进行优化.
问题是你无法调用ZeroMemory
或SecureZeroMemory
在.NET内部.在.NET中,字符串是不可变的; 你甚至不能像在其他语言中那样覆盖字符串的内容:
//Wipe out the password for (int i=0; i所以,你可以做什么?我们如何提供.NET在我们完成时从内存中擦除密码或信用卡号码的能力?
它可以完成的唯一方法是将字符串放在一些本机内存块中,然后可以调用它
ZeroMemory
.本机内存对象,例如:
一个BSTR
一个HGLOBAL
CoTaskMem非托管内存
SecureString可以恢复丢失的能力
在.NET中,完成它们后无法擦除字符串:
他们是不变的; 你不能覆盖他们的内容
你不能
Dispose
在它们他们的清理工作受到垃圾收集者的支配
SecureString作为一种传递字符串安全性的方式存在,并且能够在需要时保证它们的清理.
你问了这个问题:
为什么我不能说:
SecureString password = new SecureString("password");因为现在你有
password
记忆; 无法擦拭它.它一直停留在那里,直到CLR决定重新使用那个内存.你把我们放回了我们开始的地方; 一个正在运行的应用程序,我们无法摆脱密码,内存转储(或进程监视器)可以看到密码.SecureString使用Data Protection API来存储在内存中加密的字符串; 这样,字符串将不会存在于交换文件,崩溃转储中,甚至在局部变量窗口中,并且同事会查看你的应该.
我如何阅读密码?
然后是问题:我如何与字符串交互?你绝对不想要一个像这样的方法:
String connectionString = secureConnectionString.ToString()因为现在你回到了你开始的地方 - 一个你无法摆脱的密码.您希望强制开发人员正确处理敏感字符串 - 以便可以从内存中擦除它.
这就是为什么.NET提供了三个方便的辅助函数来将SecureString编组到非托管内存中:
SecureStringToBSTR (使用ZeroFreeCoTaskMemUnicode释放)
SecureStringToCoTaskMemUnicode (使用ZeroFreeCoTaskMemUnicode释放)
SecureStringToGlobalAllocUnicode (使用ZeroFreeGlobalAllocUnicode释放)
您将字符串转换为非托管内存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.SecurePassword属性
获取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.
4> Joe..:很少有情况可以在当前版本的Framework中明智地使用SecureString.它实际上只对与非托管API交互有用 - 您可以使用Marshal.SecureStringToGlobalAllocUnicode封送它.
一旦将它转换为System.String或从System.String转换它,你就失败了它的目的.
MSDN 示例一次从控制台输入生成SecureString一个字符,并将安全字符串传递给非托管API.这是相当复杂和不现实的.
您可能希望.NET的未来版本对SecureString有更多支持,这将使其更有用,例如:
SecureString Console.ReadLineSecure()或类似的东西将控制台输入读入SecureString,而不需要样本中的所有复杂代码.
WinForms TextBox替换,将其TextBox.Text属性存储为安全字符串,以便可以安全地输入密码.
与安全相关的API的扩展,允许密码作为SecureString传递.
如果没有上述内容,SecureString将具有有限的价值.
5> JoshReedSchr..:我相信你必须做一个字符追加而不是一个平面实例化的原因是因为在后台将"密码"传递给SecureString的构造函数会将"密码"字符串放入内存中,从而破坏安全字符串的目的.
通过追加你只是将一个角色一次放入记忆中,这种记忆可能不会彼此相邻,从而使重建原始字符串变得更加困难.我可能在这里错了,但这就是我向他解释的原因.
该类的目的是防止通过内存转储或类似工具公开安全数据.
6> kemiller2002..:MS发现在导致服务器(桌面,无论如何)崩溃的某些实例上,有时运行时环境会进行内存转储,从而暴露内存中的内容.安全字符串在内存中对其进行加密,以防止攻击者检索字符串的内容.
7> Jason Z..:SecureString的一大优点是可以避免由于页面缓存而将数据存储到磁盘的可能性。如果您的内存中有密码,然后加载了一个大型程序或数据集,则当您的程序内存不足时,您的密码可能会写入交换文件。使用SecureString,至少数据不会以明文形式无限期地驻留在磁盘上。