为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

白衣染霜花 2019-12-09 10:18:25

如果我的假设在这里错误,请随时纠正我,但是让我解释为什么我要问。

取自MSDN,是SecureString

表示应保密的文本。文本在使用时经过加密以确保隐私,并在不再需要时从计算机内存中删除。

我明白了,将密码或其他私人信息存储在SecureString上方是完全有意义的System.String,因为您可以控制将密码或其他私人信息实际存储在内存中的方式和时间,因为System.String

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

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

因此,这意味着即使用户使用密码掩码,每次用户按下一个键时,旧的字符串都会被丢弃,而新的字符串将被构建以表示文本框中的值是什么。而且我们无法控制何时或是否从内存中丢弃这些值中的任何一个

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

我的观点是:无论您做什么,SecureString被转换为System.String,这意味着它至少会在某个时刻存在于堆中(不保证任何垃圾回收)。

我的意思不是:是否有某种方法可以绕过向ssh连接发送字符串,或者可以避免使控件存储字符串(创建自定义控件)。对于这个问题,您可以将“ ssh连接”替换为“登录表”,“注册表”,“付款表”,“您要喂养的食物,而不是您的孩子的食物”,等等

  • 那么,在什么时候使用SecureString实际可行呢?

  • 完全消除System.String对象的使用是否值得花费额外的开发时间?

  • 是整点SecureString简单地减少的时间量System.String在堆上(降低其移动到物理交换文件的风险)?

  • 如果攻击者已经具备检查堆的方法,那么他很可能要么(A)已经具有读取击键的方法,要么(B)已经物理上拥有了机器 ……因此可以使用SecureString防止他进入的方法。反正数据?

  • 这仅仅是“默默无闻的安全”吗?

抱歉,如果我把问题放在太深的地方,好奇心会变得更好。随时回答我的任何或所有问题(或告诉我我的假设完全错误)。:)


查看完整描述

3 回答

?
湖上湖

实际上有非常实用的用法SecureString。


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


密码意外出现在日志文件中。

在某处显示密码-一旦GUI确实显示了正在运行的应用程序的命令行,并且该命令行包含密码。哎呀。

使用内存分析器与您的同事一起分析软件。同事在内存中看到您的密码。听起来不真实?一点也不。

我曾经使用过一种RedGate软件,该软件可以在发生异常的情况下捕获局部变量的“值”,这非常有用。不过,我可以想象它会意外记录“字符串密码”。

包含字符串密码的故障转储。

您知道如何避免所有这些问题吗?SecureString。通常可以确保您不会犯此类愚蠢的错误。如何避免呢?通过确保在非托管内存中对密码进行加密,只有在您确定自己在做什么的90%时,才可以访问真实值。


从某种意义上说,SecureString很容易工作:


1)一切都加密


2)用户通话 AppendChar


3)解密“未管理的内存”中的所有内容并添加字符


4)在“未管理的内存”中再次加密所有内容。


如果用户可以访问您的计算机怎么办?病毒是否可以访问所有SecureStrings?是。您需要做RtlEncryptMemory的就是在解密内存时陷入困境,您将获得未加密内存地址的位置,并将其读出。瞧!实际上,您可能制作了一种病毒,该病毒将不断对其进行扫描SecureString并记录所有活动。我并不是说这将是一件容易的事,但可以做到。如您所见,SecureString一旦系统中存在用户/病毒,“功能”将完全消失。


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


另外,正如其他人指出的那样,WPF支持SecureString通过其SecurePassword属性在内部使用的PasswordBox 。


底线是; 如果您有敏感数据(密码,信用卡等),请使用SecureString。这就是C#Framework所遵循的。例如,NetworkCredentialclass将密码存储为SecureString。如果您查看此内容,则可以在.NET框架中看到约80多种不同的用法SecureString。


在许多情况下,您必须转换SecureString为字符串,因为某些API会期望它。


通常的问题是:


API是通用的。它不知道有敏感数据。

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);

}

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


并且作为一个几乎与之相关的东西:Mono SecureString根本不加密。该实现已被注释掉,因为..等待它。“它以某种方式导致nunit测试损坏”,这引出了我的最后一点:


SecureString到处都不支持。如果平台/体系结构不支持SecureString,您将得到一个例外。文档中有受支持的平台列表。


查看完整回答
反对 回复 2019-12-09
?
至尊宝的传说

假设中的几个问题。


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


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


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


因此,例如使用它来通过Web进行身份验证时,您将必须使用同样安全的适当类。


在.NET Framework中,您可以使用一些类来使用SecureString


WPF的PasswordBox控件在内部将密码保留为SecureString。

System.Diagnostics.ProcessInfo的Password属性是SecureString。

X509Certificate2的构造函数使用SecureString作为密码。


总而言之,SecureString类可能很有用,但需要开发人员更多的注意。


所有这些以及示例,在MSDN的SecureString文档中都有很好的描述。


查看完整回答
反对 回复 2019-12-09
?
狐的传说

如果满足以下条件,则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)后将其清零。


查看完整回答
反对 回复 2019-12-09
  • 3 回答
  • 0 关注
  • 213 浏览
我要回答
慕课专栏
更多

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信