我什么时候需要 .NET 中的 SecureString?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/141203/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
When would I need a SecureString in .NET?
提问by Richard Morgan
I'm trying to grok the purpose of .NET's SecureString. From MSDN:
我正在尝试理解 .NET 的 SecureString 的目的。来自 MSDN:
An instance of the System.String class is both immutable and, when no longer needed, cannot be programmatically scheduled for garbage collection; that is, the instance is read-only after it is created and it is not possible to predict when the instance will be deleted from computer memory. Consequently, if a String object contains sensitive information such as a password, credit card number, or personal data, there is a risk the information could be revealed after it is used because your application cannot delete the data from computer memory.
A SecureString object is similar to a String object in that it has a text value. However, the value of a SecureString object is automatically encrypted, can be modified until your application marks it as read-only, and can be deleted from computer memory by either your application or the .NET Framework garbage collector.
The value of an instance of SecureString is automatically encrypted when the instance is initialized or when the value is modified. Your application can render the instance immutable and prevent further modification by invoking the MakeReadOnly method.
System.String 类的实例是不可变的,当不再需要时,不能以编程方式安排垃圾收集;也就是说,实例在创建后是只读的,无法预测实例何时会从计算机内存中删除。因此,如果 String 对象包含诸如密码、信用卡号或个人数据之类的敏感信息,则存在信息在使用后可能会泄露的风险,因为您的应用程序无法从计算机内存中删除数据。
SecureString 对象类似于 String 对象,因为它具有文本值。但是,SecureString 对象的值是自动加密的,可以修改,直到您的应用程序将其标记为只读,并且可以由您的应用程序或 .NET Framework 垃圾收集器从计算机内存中删除。
SecureString 实例的值在实例初始化或值被修改时自动加密。您的应用程序可以通过调用 MakeReadOnly 方法呈现实例不可变并防止进一步修改。
Is the automatic encryption the big payoff?
自动加密是最大的回报吗?
And why can't I just say:
为什么我不能说:
SecureString password = new SecureString("password");
instead of
代替
SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
pass.AppendChar(c);
What aspect of SecureString am I missing?
我缺少 SecureString 的哪些方面?
采纳答案by Joe Healy
I would stop using SecureString . Looks like PG guys are dropping support for it. Possibly even pull it in the future - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring.
我会停止使用 SecureString 。看起来PG家伙正在放弃对它的支持。甚至可能在未来拉它 - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring。
We should remove encryption from SecureString across all platforms in .NET Core - We should obsolete SecureString - We probably shouldn't expose SecureString in .NET Core
我们应该在 .NET Core 的所有平台上从 SecureString 中删除加密 - 我们应该废弃 SecureString - 我们可能不应该在 .NET Core 中公开 SecureString
回答by Chris Wenham
Some parts of the framework that currently use SecureString:
当前使用的框架的某些部分SecureString:
- WPF's
System.Windows.Controls.PasswordBoxcontrol keeps the password as a SecureString internally (exposed as a copy throughPasswordBox::SecurePassword) - The
System.Diagnostics.ProcessStartInfo::Passwordproperty is aSecureString - The constructor for
X509Certificate2takes aSecureStringfor the password
- WPF 的
System.Windows.Controls.PasswordBox控件在内部将密码保存为 SecureString(通过 公开为副本PasswordBox::SecurePassword) - 该
System.Diagnostics.ProcessStartInfo::Password物业是一个SecureString - for 的构造函数
X509Certificate2采用 aSecureString作为密码
The main purpose is to reduce the attack surface, rather than eliminate it. SecureStringsare "pinned" in RAM so the Garbage Collector won't move it around or make copies of it. It also makes sure the plain text won't get written to the Swap file or in core dumps. The encryption is more like obfuscation and won't stop a determined hacker, though, who would be able to find the symmetric keyused to encrypt and decrypt it.
主要目的是减少攻击面,而不是消除它。SecureStrings被“固定”在 RAM 中,因此垃圾收集器不会移动它或复制它。它还确保纯文本不会被写入交换文件或核心转储。加密更像是混淆,但不会阻止坚定的黑客,他们将能够找到用于加密和解密的对称密钥。
As others have said, the reason you have to create a SecureStringcharacter-by-character is because of the first obvious flaw of doing otherwise: you presumably have the secret value as a plain string already, so what's the point?
正如其他人所说,您必须SecureString逐个字符地创建一个字符的原因是因为这样做的第一个明显缺陷:您可能已经拥有作为普通字符串的秘密值,那么有什么意义呢?
SecureStrings are the first step in solving a Chicken-and-Egg problem, so even though most current scenarios require converting them back into regular strings to make any use of them at all, their existence in the framework now means better support for them in the future - at least to a point where your program doesn't have to be the weak link.
SecureStrings 是解决鸡与蛋问题的第一步,因此即使当前大多数场景都需要将它们转换回常规字符串才能使用它们,但它们在框架中的存在现在意味着更好地支持它们未来 - 至少到您的程序不必成为薄弱环节的地步。
回答by Richard Morgan
Edit: Don't use SecureString
编辑:不要使用 SecureString
Current guidance now says the class should not be used. The details can be found at this link: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
当前的指南现在说不应使用该类。详细信息可以在此链接中找到:https: //github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
From the article:
从文章:
DE0001: SecureString shouldn't be used
DE0001:不应使用 SecureString
Motivation
动机
- The purpose of
SecureStringis to avoid having secrets stored in the process memory as plain text. - However, even on Windows,
SecureStringdoesn't exist as an OS concept.- It just makes the window getting the plain text shorter; it doesn't fully prevent it as .NET still has to convert the string to a plain text representation.
- The benefit is that the plain text representation doesn't hang around
as an instance of
System.String-- the lifetime of the native buffer is shorter.
- The contents of the array is unencrypted except on .NET Framework.
- In .NET Framework, the contents of the internal char array is encrypted. .NET doesn't support encryption in all environments, either due to missing APIs or key management issues.
- 的目的
SecureString是避免将机密以纯文本形式存储在进程内存中。 - 但是,即使在 Windows 上,
SecureString也不作为操作系统概念存在。- 它只是让窗口变得更短;它并没有完全阻止它,因为 .NET 仍然需要将字符串转换为纯文本表示。
- 好处是纯文本表示不会作为一个实例
System.String- 本地缓冲区的生命周期更短。
- 除 .NET Framework 外,数组的内容未加密。
- 在 .NET Framework 中,内部字符数组的内容是加密的。由于缺少 API 或密钥管理问题,.NET 并非在所有环境中都支持加密。
Recommendation
推荐
Don't use SecureStringfor new code. When porting code to .NET Core, consider
that the contents of the array are not encrypted in memory.
不要SecureString用于新代码。将代码移植到 .NET Core 时,请考虑数组的内容在内存中未加密。
The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.
处理凭证的一般方法是避免使用凭证,而是依靠其他方式进行身份验证,例如证书或 Windows 身份验证。
End Edit : Original Summary below
结束编辑:下面的原始摘要
Lots of great answers; here's a quick synopsis of what has been discussed.
很多很棒的答案;这是所讨论内容的简要概述。
Microsoft has implemented the SecureString class in an effort to provide better security with sensitive information (like credit cards, passwords, etc.). It automatically provides:
Microsoft 已实施 SecureString 类,以努力为敏感信息(如信用卡、密码等)提供更好的安全性。它自动提供:
- encryption (in case of memory dumps or page caching)
- pinning in memory
- ability to mark as read-only (to prevent any further modifications)
- safe construction by NOT allowing a constant string to be passed in
- 加密(在内存转储或页面缓存的情况下)
- 固定在内存中
- 标记为只读的能力(以防止任何进一步修改)
- 通过不允许传入常量字符串来安全构造
Currently, SecureString is limited in use but expect better adoption in the future.
目前,SecureString 的使用受到限制,但预计将来会得到更好的采用。
Based on this information, the constructor of the SecureString should not just take a string and slice it up to char array as having the string spelled out defeats the purpose of SecureString.
根据这些信息,SecureString 的构造函数不应只是获取一个字符串并将其切成字符数组,因为将字符串拼写出来会违背 SecureString 的目的。
Additional info:
附加信息:
- A postfrom the .NET Security blog talking about much the same as covered here.
- And another onerevisiting it and mentioning a tool that CAN dump the contents of the SecureString.
Edit: I found it tough to pick the best answer as there's good information in many; too bad there is no assisted answer options.
编辑:我发现很难选出最佳答案,因为很多资料都有很好的信息;太糟糕了,没有辅助答案选项。
回答by Ian Boyd
Short Answer
简答
why can't I just say:
SecureString password = new SecureString("password");
为什么我不能说:
SecureString password = new SecureString("password");
Because now you have passwordin memory; with no way to wipe it - which is exactly the point of SecureString.
因为现在你password在记忆中;无法擦除它 - 这正是SecureString 的重点。
Long Answer
长答案
The reason SecureStringexists is because you cannot use ZeroMemoryto wipe sensitive data when you're done with it. It exists to solve an issue that exists becauseof the CLR.
SecureString存在的原因是因为您无法使用ZeroMemory在完成后擦除敏感数据。它的存在是为了解决因CLR而存在的问题。
In a regular nativeapplication you would call SecureZeroMemory:
在常规的本机应用程序中,您将调用SecureZeroMemory:
Fills a block of memory with zeros.
用零填充内存块。
Note: SecureZeroMemory is is identical to ZeroMemory, except the compiler won't optimize it away.
注意: SecureZeroMemory 与 相同ZeroMemory,只是编译器不会优化它。
The problem is that you can'tcall ZeroMemoryor SecureZeroMemoryinside .NET. And in .NET strings are immutable; you can't even overwritethe contents of the string like you can do in other languages:
问题是你不能在 .NET 中调用ZeroMemory或SecureZeroMemory。在 .NET 中,字符串是不可变的;你甚至不能像在其他语言中那样覆盖字符串的内容:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = SecureString password = new SecureString("password");
;
So what can you do? How do we provide the ability in .NET to wipe a password, or credit card number from memory when we're done with it?
所以,你可以做什么?我们如何在 .NET 中提供从内存中擦除密码或信用卡号的能力?
The only way it can be done would be to place the string in some nativememory block, where you canthen call ZeroMemory. A native memory object such as:
唯一可以做到的方法是将字符串放在某个本机内存块中,然后您可以在其中调用ZeroMemory. 本机内存对象,例如:
- a BSTR
- an HGLOBAL
- CoTaskMem unmanaged memory
- 一个BSTR
- 一个 HGLOBAL
- CoTaskMem 非托管内存
SecureString gives the lost ability back
SecureString 恢复失去的能力
In .NET, Strings cannot be wiped when you are done with them:
在 .NET 中,使用完字符串后无法擦除字符串:
- they are immutable; you cannot overwrite their contents
- you cannot
Disposeof them - their cleanup is at the mercy of the garbage collector
- 它们是不可变的;你不能覆盖他们的内容
- 你不能
Dispose对他们 - 他们的清理工作由垃圾收集器支配
SecureString exists as a way to pass around strings safety, and be able to guarantee their cleanup when you need to.
SecureString 作为传递字符串安全的一种方式存在,并且能够在需要时保证它们的清理。
You asked the question:
你问了这个问题:
why can't I just say:
SecureString password = new SecureString("password");
为什么我不能说:
String connectionString = secureConnectionString.ToString()
Because now you have passwordin memory; with no way to wipe it. It's stuck there until the CLR happens to decide to re-use that memory. You've put us right back where we started; a running application with a password we can't get rid of, and where a memory dump (or Process Monitor) can see the password.
因为现在你password在记忆中;没有办法擦掉它。它一直卡在那里,直到 CLR 碰巧决定重新使用该内存。你让我们回到了起点;一个正在运行的应用程序,带有我们无法摆脱的密码,以及内存转储(或进程监视器)可以看到密码的地方。
SecureString uses the Data Protection API to store the string encrypted in memory; that way the string will not exist in swapfiles, crash dumps, or even in the local variables window with a colleague looking over your should.
SecureString 使用数据保护 API 将加密的字符串存储在内存中;这样字符串就不会存在于交换文件、崩溃转储中,甚至不会存在于同事查看您的应有的局部变量窗口中。
How do i read the password?
我如何读取密码?
Then is the question: how do i interact with the string? You absolutely don'twant a method like:
那么问题来了:我如何与字符串交互?你绝对不想要这样的方法:
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
because now you're right back where you started - a password you cannot get rid of. You want to forcedevelopers to handle the sensitive string correctly - so that it canbe wiped from memory.
因为现在你又回到了开始的地方——一个你无法摆脱的密码。您想强制开发人员正确处理敏感字符串 - 以便它可以从内存中擦除。
That is why .NET provides three handy helper functions to marshall a SecureString into a unmanaged memory:
这就是为什么 .NET 提供了三个方便的辅助函数来将 SecureString 编组到非托管内存中:
- SecureStringToBSTR(freed with ZeroFreeCoTaskMemUnicode)
- SecureStringToCoTaskMemUnicode(freed with ZeroFreeCoTaskMemUnicode)
- SecureStringToGlobalAllocUnicode(freed with ZeroFreeGlobalAllocUnicode)
- SecureStringToBSTR (使用ZeroFreeCoTaskMemUnicode释放)
- SecureStringToCoTaskMemUnicode (使用ZeroFreeCoTaskMemUnicode释放)
- SecureStringToGlobalAllocUnicode (使用ZeroFreeGlobalAllocUnicode释放)
You convert the string into an unmanaged memory blob, handle it, and then wipe it again.
您将字符串转换为非托管内存 blob,处理它,然后再次擦除它。
Some APIs accept SecureStrings. For example in ADO.net 4.5 the SqlConnection.Credentialtakes a set SqlCredential:
一些 API 接受SecureStrings。例如,在 ADO.net 4.5 中,SqlConnection.Credential接受一组SqlCredential:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
You can also change the password within a Connection String:
您还可以更改连接字符串中的密码:
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;
}
And there are a lot of places inside .NET where they continue to accept a plain String for compatibility purposes, then quickly turn around an put it into a SecureString.
.NET 中有很多地方为了兼容性的目的继续接受纯字符串,然后迅速将其转换为 SecureString。
How to put text into the SecureString?
如何将文本放入 SecureString?
This still leaves the problem:
这仍然留下了问题:
How do i get a password into the SecureString in the first place?
我如何首先将密码输入 SecureString?
This is the challenge, but the point is to get you thinking about security.
这是挑战,但重点是让您考虑安全性。
Sometimes the functionality is already provided for you. For example, the WPF PasswordBoxcontrol can return you the entered password as a SecureStringdirectly:
有时该功能已经为您提供。例如,WPF PasswordBox控件可以直接将输入的密码作为SecureString返回:
PasswordBox.SecurePassword Property
Gets the password currently held by the PasswordBoxas a SecureString.
PasswordBox.SecurePassword 属性
获取 PasswordBox 当前持有的密码作为SecureString。
This is helpful because everywhere you used to pass around a raw string, you now have the type system complaining that SecureString is incompatible with String. You want to go as long as possible before having to convert your SecureString back into regular string.
这很有用,因为在您过去传递原始字符串的任何地方,现在都有类型系统抱怨 SecureString 与 String 不兼容。在必须将 SecureString 转换回常规字符串之前,您希望尽可能长。
Converting a SecureString is easy enough:
转换 SecureString 很容易:
- SecureStringToBSTR
- PtrToStringBSTR
- 安全字符串到BSTR
- PtrToStringBSTR
as in:
如:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
They just really don't want you doing it.
他们只是真的不想让你这样做。
But how do i get a string into a SecureString? Well what you need to do is stop having a password in a Stringin the first place. You needed to have it in somethingelse. Even a Char[]array would be helpful.
但是如何将字符串转换为 SecureString?那么你需要做的是首先停止在字符串中输入密码。你需要把它放在别的东西里。即使是Char[]数组也会有帮助。
That's when you can append each character andwipe the plaintext when you're done:
那时你可以附加每个字符并在完成后擦除明文:
##代码##You need your password stored in somememory that you can wipe. Load it into the SecureString from there.
您需要将密码存储在一些可以擦除的内存中。从那里将其加载到 SecureString 中。
tl;dr: SecureStringexists to provide the equivalent of ZeroMemory.
tl;dr: SecureString 的存在是为了提供ZeroMemory的等价物。
Some people don't see the point in wiping the user's password from memory when a device is locked, or wiping wiping keystrokes from memory after they'authenticated. Those people do not use SecureString.
有些人不认为在设备锁定时从内存中擦除用户密码,或在用户通过身份验证后从内存中擦除按键操作的意义。那些人不使用 SecureString。
回答by Joe
There are very few scenarios where you can sensibly use SecureString in the current version of the Framework. It's really only useful for interacting with unmanaged APIs - you can marshal it using Marshal.SecureStringToGlobalAllocUnicode.
在当前版本的框架中,很少有可以明智地使用 SecureString 的场景。它实际上只对与非托管 API 交互有用 - 您可以使用 Marshal.SecureStringToGlobalAllocUnicode 对其进行编组。
As soon as you convert it to/from a System.String, you've defeated its purpose.
一旦您将其转换为 System.String 或从 System.String 转换,您就违背了它的目的。
The MSDN samplegenerates the SecureString a character at a time from console input and passes the secure string to an unmanaged API. It's rather convoluted and unrealistic.
MSDN示例从控制台输入一次生成一个字符的 SecureString,并将安全字符串传递给非托管 API。这是相当复杂和不现实的。
You might expect future versions of .NET to have more support for SecureString that will make it more useful, e.g.:
您可能希望 .NET 的未来版本对 SecureString 有更多支持,这将使其更有用,例如:
SecureString Console.ReadLineSecure() or similar to read console input into a SecureString without all the convoluted code in the sample.
WinForms TextBox replacement that stores its TextBox.Text property as a secure string so that passwords can be entered securely.
Extensions to security-related APIs to allow passwords to be passed as SecureString.
SecureString Console.ReadLineSecure() 或类似的将控制台输入读取到 SecureString 中,而无需示例中的所有复杂代码。
WinForms TextBox 替代品,将其 TextBox.Text 属性存储为安全字符串,以便可以安全地输入密码。
安全相关 API 的扩展,允许将密码作为 SecureString 传递。
Without the above, SecureString will be of limited value.
没有上述内容, SecureString 的价值将是有限的。
回答by JoshReedSchramm
I believe the reason why you have to do character appending instead of one flat instantiation is because in the background passing "password" to the constructor of SecureString puts that "password" string in memory defeating the purpose of secure string.
我相信您必须进行字符追加而不是一个平面实例化的原因是因为在后台将“密码”传递给 SecureString 的构造函数会将“密码”字符串放入内存中,从而违背了安全字符串的目的。
By appending you are only putting a character at a time into memory which is likley not to be adjacent to each other physically making it much harder to reconstruct the original string. I could be wrong here but that's how it was explained to me.
通过附加,您一次只将一个字符放入内存中,这很可能不会在物理上彼此相邻,从而使重建原始字符串变得更加困难。我在这里可能是错的,但这就是向我解释的方式。
The purpose of the class is to prevent secure data from being exposed via a memory dump or similar tool.
该类的目的是防止通过内存转储或类似工具暴露安全数据。
回答by kemiller2002
MS found that on certain instances of causing the server (desktop, whatever) to crash there were times when the runtime environment would do a memory dump exposing the contents of what's in memory. Secure String encrypts it in memory to prevent the attacker from being able to retrieve the contents of the string.
MS 发现,在某些导致服务器(桌面等)崩溃的情况下,有时运行时环境会进行内存转储,从而暴露内存中的内容。安全字符串在内存中对其进行加密,以防止攻击者能够检索字符串的内容。
回答by Jason Z
One of the big benefits of a SecureString is that it is supposed avoid the possibility of your data being stored to disk due to page caching. If you have a password in memory and then load a large program or data set, your password may get written to the swap file as your program is paged out of memory. With a SecureString, at least the data will not be sitting around indefinitely on your disk in clear text.
SecureString 的一大好处是它可以避免由于页面缓存而将数据存储到磁盘的可能性。如果你有记忆密码,然后装入大型程序或数据集,为你的程序的内存调出你的密码可能会写入交换文件。使用 SecureString,至少数据不会以明文形式无限期地留在磁盘上。
回答by Mark Bessey
Well, as the description states, the value is stored encrypted, with means that a memory dump of your process won't reveal the string's value (without some fairly serious work).
好吧,正如描述所述,该值是加密存储的,这意味着您的进程的内存转储不会显示字符串的值(没有一些相当认真的工作)。
The reason you can't just construct a SecureString from a constant string is because then you wouldhave an unencrypted version of the string in memory. Limiting you to creating the string in pieces reduces the risk of having the whole string in memory at once.
您不能仅从常量字符串构造 SecureString 的原因是因为这样您将在内存中拥有该字符串的未加密版本。限制你创造片串减少有一次在内存中的整个字符串的风险。
回答by OregonGhost
I guess it's because the string is meant to be secure, i.e. a hacker should not be able to read it. If you initialize it with a string, the hacker could read the original string.
我想这是因为该字符串是安全的,即黑客不应该能够读取它。如果用字符串初始化它,黑客可以读取原始字符串。

