SecureString在C#应用程序中是否实用?

Ste*_*ies 218 c# security

如果我的假设在这里错了,请随意纠正我,但让我解释一下我为什么要问.

摘自MSDN,a SecureString:

代表应保密的文本.文本在使用时加密以保护隐私,并在不再需要时从计算机内存中删除.

我明白了,将密码或其他私人信息存储在a中是完全合理SecureStringSystem.String,因为你可以控制它实际存储在内存中的方式和时间,因为System.String:

是不可变的,当不再需要时,不能以编程方式安排垃圾收集; 也就是说,实例在创建后是只读的,并且无法预测实例何时从计算机内存中删除.因此,如果String对象包含敏感信息(如密码,信用卡号或个人数据),则使用该信息后可能会显示该信息,因为您的应用程序无法从计算机内存中删除该数据.

但是,在GUI应用程序(例如,ssh客户端)的SecureString 情况下,必须从a构建 System.String.所有文本控件都使用字符串作为其基础数据类型.

因此,这意味着每次用户按下某个键时,就会丢弃那里的旧字符串,并构建一个新字符串来表示文本框内的值,即使使用密码掩码.并且我们无法控制何时或是否从内存中丢弃任何这些值.

现在是时候登录服务器了.你猜怎么着?您需要在连接上传递一个字符串以进行身份​​验证.所以让我们将我们SecureString转换为System.String....现在我们在堆上有一个字符串,无法强制它通过垃圾收集(或将0写入其缓冲区).

我的观点是:无论你做什么,沿线某处,那SecureString被转换成一个System.String,这意味着它至少会在某些点上堆存在(没有垃圾收集的任何保证).

我的观点不是:是否存在绕过将字符串发送到ssh连接或绕过控件存储字符串(制作自定义控件)的方法.对于这个问题,您可以将"ssh connection"替换为"登录表单","注册表单","付款表单","食物 - 你愿意喂你的小狗但不是你孩子的表格",等等

  • 那么,在什么时候使用SecureString实际变得实用?
  • 是否值得花费额外的开发时间来彻底根除System.String对象的使用?
  • 完全重点是SecureString减少System.String堆上的时间(降低转移到物理交换文件的风险)?
  • 如果攻击者已经拥有进行堆检查的手段,那么他很可能(A)已经有了读取击键的方法,或者(B)已经在物理上拥有该机器 ...所以会使用a SecureString阻止他进入数据呢?
  • 这只是"通过默默无闻的安全"吗?

对不起,如果我把问题放得太厚,好奇心就好了.随意回答我的任何或所有问题(或告诉我,我的假设是完全错误的).:)

Ert*_*maa 230

实际上有非常实际的用途SecureString.

你知道我见过这种情景多少次了吗?(答案是:很多!):

  • 密码意外地出现在日志文件中.
  • 密码正在某处显示 - 一旦GUI显示正在运行的应用程序命令行,并且命令行由密码组成.哎呀.
  • 使用内存分析器与您的同事分析软件.同事在内存中看到您的密码.听起来不真实吗?一点也不.
  • 我曾经使用的RedGate软件可以捕获局部变量的"值",以防异常,非常有用.虽然,我可以想象它会意外地记录"字符串密码".
  • 包含字符串密码的故障转储.

你知道如何避免所有这些问题吗?SecureString.它通常会确保你不会犯这样的愚蠢错误.它是如何避免它的?通过确保密码在非托管内存中加密,只有在90%确定您正在执行的操作时才能访问实际值.

从某种意义上说,SecureString非常容易:

1)一切都是加密的

2)用户来电 AppendChar

3)解密UNMANAGED MEMORY中的所有内容并添加角色

4)在UNMANAGED MEMORY中再次加密所有内容.

如果用户可以访问您的计算机怎么办?病毒是否能够访问所有病毒SecureStrings?是.您需要做RtlEncryptMemory的就是在解密内存时自己进入,您将获得未加密的内存地址的位置,并将其读出.瞧!实际上,您可以制作一种病毒,该病毒会不断扫描其使用情况SecureString并记录所有活动.我并不是说这将是一件容易的事,但可以做到.正如您所看到的,SecureString一旦系统中存在用户/病毒,"强大" 就完全消失了.

你的帖子中有几点.当然,如果您在内部使用一些保存"字符串密码"的UI控件,那么使用实际SecureString操作并不是那么有用.虽然,它仍然可以防止我上面列出的一些愚蠢.

此外,正如其他人所说,WPF支持PasswordBox,它SecureString通过其SecurePassword属性在内部使用.

底线是; 如果您有敏感数据(密码,信用卡,..),请使用SecureString.这就是C#Framework所遵循的.例如,NetworkCredential类将密码存储为SecureString.如果你看看这个,你可以看到在在.NET框架〜80种不同的用途SecureString.

在许多情况下,您必须转换SecureString为字符串,因为某些API需要它.

通常的问题是:

  1. API是GENERIC.它不知道有敏感的数据.
  2. API知道它处理敏感数据并使用"字符串" - 这只是糟糕的设计.

你提出了一个好点:SecureString转换成什么时会发生什么string?这只能因为第一点而发生.例如,API不知道它是敏感数据.我个人没有看到过这种情况.从SecureString中获取字符串并不是那么简单.

由于一个简单的原因,这并不简单 ; 它从来没有打算让用户将SecureString转换为字符串,正如你所说:GC会启动.如果你看到自己这样做,你需要退后一步问自己:为什么我甚至这样做,或者我真的需要这个,为什么?

我看到了一个有趣的案例.即,WinApi函数LogonUser将LPTSTR作为密码,这意味着您需要调用SecureStringToGlobalAllocUnicode.这基本上为您提供了未加密的密码,该密码位于非托管内存中.一旦你完成,你需要摆脱它:

// Marshal the SecureString to unmanaged memory.
IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password);
try
{
   //...snip...
}
finally 
{
   // Zero-out and free the unmanaged string reference.
   Marshal.ZeroFreeGlobalAllocUnicode(rawPassword);
}
Run Code Online (Sandbox Code Playgroud)

您始终可以SecureString使用扩展方法扩展该类,例如ToEncryptedString(__SERVER__PUBLIC_KEY),使用服务器的公钥加密您的string实例SecureString.只有服务器可以解密它.问题解决了:垃圾收集永远不会看到"原始"字符串,因为您从未在托管内存中公开它.这正是在PSRemotingCryptoHelper(EncryptSecureStringCore(SecureString secureString))中所做的.

而且几乎相关的东西:Mono SecureString根本不加密.该实现已被注释掉,因为..等待它.. "它以某种方式导致nunit测试破坏",这带来了我的最后一点:

SecureString在任何地方都不受支持.如果平台/体系结构不支持SecureString,您将获得异常.文档中支持的平台列表.

  • 我在执行的每个源代码审计中都标记了这一点。还应该重复的是,如果您使用 `SecureString`,它需要在整个堆栈中一直使用。 (2认同)
  • [Microsoft 的新文档指出“SecureString”不应用于新开发](https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netframework-4.8 #评论)? (2认同)

Dam*_*ash 15

在你的假设中几乎没有问题.

首先,SecureString类没有String构造函数.为了创建一个,你分配一个对象,然后附加字符.

对于GUI或控制台,您可以非常轻松地将每个按下的键传递给安全字符串.

该类的设计方式是您不能错误地访问存储的值.这意味着您无法string直接从中获取密码.

因此,对于使用它,例如,通过Web进行身份验证,您将必须使用也是安全的适当类.

在.NET框架中,您有一些可以使用SecureString的类

  • WPF的PasswordBox控件在内部将密码保存为SecureString.
  • System.Diagnostics.ProcessInfo的Password属性是SecureString.
  • X509Certificate2的构造函数使用SecureString作为密码.

(更多)

总而言之,SecureString类可能很有用,但需要开发人员给予更多关注.

所有这些,以及示例,都在MSDN的SecureString文档中有详细描述

  • 我不太明白你答案的第三段到最后一段.*如何通过网络传输`SecureString`而不将其序列化为`string`?我认为OP的问题在于,有一段时间你想要实际*使用'SecureString`中安全保存的值,这意味着你必须把它从那里拿出来,而且它已经不再安全了.在所有的Framework类库中,几乎没有方法可以直接接受`SecureString`作为输入(据我所知),那么首先在一个`SecureString`中保存一个值是什么意思? (9认同)

Joe*_*Joe 10

在以下情况下,SecureString非常有用:

  • 您逐个字符地构建它(例如从控制台输入)或从非托管API获取它

  • 您可以通过将其传递给非托管API(SecureStringToBSTR)来使用它.

如果你将它转换为托管字符串,你就已经失败了.

更新以回应评论

......或者像你提到的BSTR,这似乎不再安全

在转换为BSTR之后,使用BSTR的非托管组件可以将内存归零.非托管内存在以这种方式重置的意义上更安全.

但是,.NET Framework中支持SecureString的API很少,因此您可以说它今天的价值非常有限.

我将看到的主要用例是在客户端应用程序中,要求用户输入高度敏感的代码或密码.可以逐个字符地使用用户输入来构建SecureString,然后可以将其传递给非托管API,该API在使用它之后将其接收的BSTR归零.任何后续内存转储都不包含敏感字符串.

在服务器应用程序中,很难看出它在哪里有用.

更新2

接受SecureString的.NET API的一个示例是X509Certificate类的构造函数.如果您使用ILSpy或类似的东西,您将看到SecureString在内部转换为非托管缓冲区(Marshal.SecureStringToGlobalAllocUnicode),然后在完成(Marshal.ZeroFreeGlobalAllocUnicode)时归零.

  • +1,但是你不会总是以“违背其目的”告终吗?鉴于框架类库中几乎没有方法接受“SecureString”作为输入,它们的用途是什么?您迟早总是必须转换回“string”(或像您提到的“BSTR”,这似乎不再安全)。那么为什么要费心使用“SecureString”呢? (2认同)

SᴇM*_*SᴇM 9

Microsoft 不建议将其SecureString用于较新的代码。

SecureString Class 的文档中:

重要的

我们不建议您将该SecureString类用于新的开发。有关更多信息,请参阅SecureString不应使用

其中推荐:

不要SecureString用于新代码。将代码移植到 .NET Core 时,请考虑数组的内容在内存中未加密。

处理凭证的一般方法是避免使用凭证,而是依靠其他方式进行身份验证,例如证书或 Windows 身份验证。在 GitHub 上。