SecureString在C#应用程序中是否实用?
如果我的假设在这里是错误的,请随时纠正我,但让我解释我为什么问。
从MSDN取得一个SecureString
:
代表应保密的文字。 该文本在使用时被加密以保护隐私,并在不再需要时从计算机内存中删除。
我明白了,通过System.String
在SecureString
存储密码或其他私人信息是完全有意义的,因为您可以控制它如何以及何时实际存储在内存中,因为System.String
:
是不可变的,当不再需要时,不能以编程方式安排垃圾收集; 也就是说,实例在创建后是只读的,并且无法预测实例何时从计算机内存中删除。 因此,如果字符串对象包含密码,信用卡号码或个人数据等敏感信息,则信息在使用后可能会被泄露,因为您的应用程序无法从计算机内存中删除数据。
但是,对于GUI应用程序(例如,ssh客户端),必须从System.String
构建SecureString
。 所有的文本控件都使用字符串作为其基础数据类型。
因此,这意味着每次用户按下某个键时,旧的字符串都会被丢弃,并且会构建一个新字符串来表示文本框内的值,即使使用密码掩码也是如此。 而且我们无法控制何时或是否有任何这些值从内存中被丢弃。
现在该登录到服务器了。 你猜怎么了? 您需要在连接上传递字符串以进行身份验证。 因此,让我们将SecureString
转换为System.String
....现在我们在堆上有一个字符串,无法强制它通过垃圾回收(或将0写入其缓冲区)。
我的观点是 :不管你做什么,在某个地方, SecureString
将被转换成一个System.String
,这意味着它至少会在堆上存在(没有任何垃圾收集保证)。
我的观点不是 :是否有方法避免向ssh连接发送字符串,或者绕过控制存储字符串(进行自定义控制)。 对于这个问题,你可以用“登录表单”,“注册表单”,“付款表单”,“食物 - 你会喂小孩但不是你的孩子的形式”代替“ssh连接”,等等
SecureString
实际上变得实用? System.String
对象的使用? SecureString
是简单地减少一个System.String
在堆上的时间(减少它转移到物理交换文件的风险)? SecureString
防止他进入反正数据? 对不起,如果我对太厚的问题提出疑问,好奇心就会变得越来越好。 随时回答我的任何或全部问题(或告诉我,我的假设是完全错误的)。 :)
实际上SecureString
非常实用。
你知道有多少次我见过这样的场景吗? (答案是:很多!):
RedGate
软件捕捉局部变量的“值”,以防出现异常,这非常有用。 虽然,我可以想象它会不小心记录“字符串密码”。 你知道如何避免所有这些问题吗? SecureString
。 它通常确保你不会犯这样的愚蠢错误。 它如何避免它? 通过确保密码在非托管内存中被加密,并且只有当您90%确定自己在做什么时才能访问真实值。
从某种意义上说, SecureString
工作非常简单:
1)一切都被加密
2)用户调用AppendChar
3)解密UNMANAGED MEMORY中的所有内容并添加字符
4)在UNMANAGED MEMORY中再次加密所有内容。
如果用户有权访问您的计算机,该怎么办? 病毒能够访问所有的SecureStrings
吗? 是。 所有你需要做的就是在内存被解密的时候把你自己插入到RtlEncryptMemory
,你将得到未加密内存地址的位置并读出它。 瞧! 事实上,你可能会制造一种病毒,不断扫描SecureString
使用情况并记录所有活动。 我并不是说这将是一件容易的事,但它可以完成。 正如你所看到的,一旦系统中存在用户/病毒, SecureString
的“强大功能”就完全消失了。
你的帖子中有几点。 当然,如果你使用一些内部持有“字符串密码”的UI控件,使用实际的SecureString
并不是那么有用。 尽管如此,它仍然可以防止我上面列出的某些愚蠢行为。
另外,正如其他人所指出的,WPF支持通过其SecurePassword属性在内部使用SecureString
PasswordBox。
底线是; 如果您有敏感数据(密码,信用卡等),请使用SecureString
。 这就是C#框架所遵循的。 例如, NetworkCredential
类将密码存储为SecureString
。 如果你看看这个,你可以在SecureString
.NET框架中看到SecureString
种不同的用法。
有很多情况下,您必须将SecureString
转换为字符串,因为有些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)
,该扩展方法可为您提供使用服务器公钥加密的SecureString
string
实例。 只有服务器才能解密它。 解决的问题:垃圾收集永远不会看到“原始”字符串,因为您从不在托管内存中公开它。 这正是PSRemotingCryptoHelper
( EncryptSecureStringCore(SecureString secureString)
) EncryptSecureStringCore(SecureString secureString)
。
和几乎相关的东西: Mono SecureString根本不加密 。 实施已被评论,因为..等待它..“它以某种方式导致nunit测试破损”,这引起了我的最后一点:
SecureString
在任何地方都不受支持。 如果平台/体系结构不支持SecureString
,则会发生异常。 文档中有一个支持的平台列表。
你的假设中很少有问题。
首先,SecureString类没有String构造函数。 为了创建一个你分配一个对象,然后附加字符。
在GUI或控制台的情况下,您可以非常轻松地将每个按下的按键传递给安全的字符串。
这个类的设计方式是你不能错误地访问存储的值。 这意味着你不能直接从它获取string
作为密码。
因此,为了使用它,例如,通过网络进行身份验证,您将不得不使用适当的类也是安全的。
在.NET框架中,您有几个可以使用SecureString的类
(更多)
总之,SecureString类可能很有用,但需要开发人员更多的关注。
所有这些,都带有示例,在MSDN的SecureString文档中有很好的描述
在以下情况下,SecureString很有用:
你可以通过字符(例如从控制台输入)来构建它,或者从非托管API中获取它
通过将它传递给非托管API(SecureStringToBSTR)来使用它。
如果你曾经将它转换为托管字符串,那么你已经击败了它的目的。
更新回应评论
......或者像你提到的BSTR,这似乎没有更安全的
在将其转换为BSTR后,使用BSTR的非托管组件可以清零内存。 非托管内存在这种意义上可以被重置的意义上更安全。
但是,.NET Framework中支持SecureString的API很少,因此您今天说它的价值非常有限。
我将看到的主要用例是在需要用户输入高度敏感的代码或密码的客户端应用程序中。 用户输入可以逐字符地使用来构建SecureString,然后可以将其传递给非托管API,这使得它在使用它之后收到的BSTR为零。 任何后续内存转储将不包含敏感字符串。
在一个服务器应用程序中,很难看到它在哪里有用。
更新2
接受SecureString的.NET API的一个示例是X509Certificate类的此构造函数。 如果你使用ILSpy或类似的东西,你会看到SecureString在内部被转换成一个非托管缓冲区( Marshal.SecureStringToGlobalAllocUnicode
),当完成( Marshal.ZeroFreeGlobalAllocUnicode
)时,它将被清零。
上一篇: Is SecureString ever practical in a C# application?
下一篇: Best ways to secure form data from malicious users wielding Firebug?