我试图神交.net的SecureString的目的。 从MSDN:
System.String类的实例是不可变的,并且不再需要时,不能以编程方式安排垃圾回收; 也就是说,该实例是只读的创建后,它无法预测何时实例将从计算机内存中删除。 因此,如果一个String对象包含敏感信息,如密码,信用卡号码或个人数据,是有风险的使用后会因为你的应用程序无法删除从计算机内存中的数据的信息可以透露。
甲SecureString的对象类似于在于它具有文本值字符串对象。 然而,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中“固定”这样的垃圾收集器将不会左右移动,或使副本。 这也确保了纯文本将不会被写入交换文件或核心转储。 加密更像是迷惑,也不会阻止确定的黑客,但是,将能够找到谁对称密钥用于加密和解密。
正如其他人所说的原因,你必须创建一个SecureString
字符一个字符,否则这样做的第一个明显的缺陷:你大概有秘密值作为纯字符串了,所以有什么意义呢?
SecureString
s为解决一个鸡和蛋的问题的第一步,所以尽管目前大多数情况下需要将它们放回普通字符串作任何利用他们所有,他们在框架存在现在意味着为他们提供更好的支持未来 - 至少到一个地步,你的程序不必是薄弱环节。
伟大的答案大量的; 这里是一个什么样已经讨论过的快速概要。
微软已经实施SecureString类在努力为用户提供敏感信息(如信用卡,密码等)更好的安全性。 它自动提供:
- 加密(在存储器情况下转储或页面缓存)
- 钉扎在内存
- 能力来标记为只读(以防止任何进一步的修改)
- 安全施工不容许一个常量字符串传递中
目前,SecureString的是利用有限但预计在将来更好采纳。
基于这些信息,SecureString的的构造函数不应该只是一个字符串并将切它最多字符数组具有阐明的字符串击败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<password.Length; i++)
password[i] = \0;
所以,你可以做什么? 我们如何提供.NET擦拭密码,或从存储信用卡号码,当我们用它做的能力吗?
这是可以做到的唯一办法是将字符串中一些本机内存块,在那里你可以调用ZeroMemory
。 原生存储器对象,如:
- 一个BSTR
- 一个HGLOBAL
- CoTaskMem非托管内存
SecureString的给失去的能力回
在.NET中,字符串不能当你与他们进行擦拭:
- 他们是不可变的; 你不能覆盖其内容
- 你不能
Dispose
他们的 - 他们的清理是在垃圾收集器的摆布
SecureString的存在,以此来传递字串,安全性,并能够当你需要保证他们的清理工作。
你问的问题:
为什么我不能只是说:
SecureString password = new SecureString("password");
因为现在你有password
在内存中; 有没有办法来消灭它。 直到CLR正好决定重新使用该内存它卡在那里。 你把我们又回到我们开始的地方; 用密码,我们无法摆脱的,并在内存转储(或进程监视器)可以看到密码运行的应用程序。
SecureString的使用数据保护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的是字符串不兼容。 你想尽可能长的时间去为你的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的? 那么你需要做的是停止其在首位字符串密码。 你需要有它在别的东西。 即使一个Char[]
阵列将是有益的。
这时候,你可以追加每个字符,并擦拭明文当你完成:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
你需要存储在一些内存,你可以消灭你的密码。 其装载到SecureString的从那里。
TL; DR:SecureString的存在提供ZeroMemory的等价物。
有些人不看到一点从内存抹用户的密码,当设备被锁定 ,或者擦they'authenticated后从内存抹按键 。 那些人不使用SecureString的。
有极少数的情况下,您可以在合理的框架的当前版本使用SecureString的。 这真的只是为了与非托管API的交互有用的 - 你可以使用Marshal.SecureStringToGlobalAllocUnicode名帅它。
只要你将它转换为/从以System.String,你已经打败了它的目的。
在MSDN 样品生成SecureString的在从控制台输入的时间的字符,并传递安全字符串到非托管API。 这是颇为曲折的和不现实的。
你可能期望.NET的未来版本中有SecureString的更多支持,这将使它更加有用的,例如:
SecureString的Console.ReadLineSecure()或类似阅读控制台输入到SecureString的,而不与样品中所有的卷积码。
的WinForms文本框更换存储其TextBox.Text财产安全的字符串,以便密码可以安全地进入。
扩展到与安全相关的API,以便密码作为SecureString的传递。
没有上述,SecureString的将是有限的价值。
我相信,因为在后台通过“密码” SecureString的的构造使在记忆的“密码”字符串击败安全字符串的目的,为什么你要做的字符附加,而不是一个平坦的实例化的原因。
通过追加你只把一个角色在同一时间到内存是likley不被相邻的身体使它更难重建原始的字符串。 我可能是错在这里但这是它是如何向我解释。
类的目的是为了防止从经由存储器转储或类似工具被暴露的安全数据。
MS发现,导致服务器的某些情况下(台式机,无论)崩溃有些时候运行环境会做内存转储露出什么是在内存中的内容。 安全字符串加密在内存中,以防止攻击者能够获取字符串的内容。
其中的SecureString的很大的好处是,它是应该避免你的数据的可能性被存储到磁盘由于页面缓存。 如果你有记忆密码,然后装入大型程序或数据集,为你的程序调出的存储你的密码可能会写入交换文件。 随着SecureString的,至少数据不会无限期地以明文形式在磁盘上坐着。
我想这是因为该字符串,就是要安全,即黑客不应该能够阅读它。 如果你用字符串初始化它,黑客可以读取原始字符串。
那么,作为描述状态,值存储加密的,与你的进程的内存转储不会透露字符串的值(不包括一些相当严肃的工作)的手段。
你不能只从一个常量字符串构建一个SecureString的原因是因为你将不得不在内存中的串的未加密版本。 限制你创造片串减少一次在内存中整个字符串的风险。
我将停止使用SecureString的。 看起来PG家伙下降对它的支持。 甚至可能把它的未来- https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring 。
我们应该从.NET的核心在所有平台SecureString的删除加密 - 我们要过时的SecureString的 - 我们也许不应该在.NET核心暴露SecureString的
另一个用例是当你与支付应用(POS)工作,你根本就不是为了存储敏感数据,因为你很小心,开发使用一成不变的数据结构 。 例如:如果我将敏感数据卡或授权的元数据存储到不变的串点,会当这个数据在内存中的时间显著量可用时,它被丢弃后的情况。 我不能简单地将其覆盖。 另一个巨大的优势,其中这样的敏感数据被保存在存储器中加密。