问题描述
如果我的假设有误,请随时纠正我,但让我解释一下我问的原因.
Feel free to correct me if my assumptions are wrong here, but let me explain why I'm asking.
取自 MSDN,一个 SecureString
:
Taken from MSDN, a SecureString
:
代表应该保密的文本.文本在使用时进行隐私加密,不再需要时从计算机内存中删除.
我明白了,将密码或其他私人信息存储在 SecureString
中的 System.String
中是完全有意义的,因为您可以控制它的方式和时间实际上是存放在内存中的,因为一个System.String
:
I get this, it makes complete sense to store a password or other private information in a SecureString
over a System.String
, because you can control how and when it is actually stored in memory, because a System.String
:
是不可变的,当不再需要时,不能以编程方式安排垃圾收集;也就是说,实例在创建后是只读的,无法预测实例何时会从计算机内存中删除.因此,如果 String 对象包含诸如密码、信用卡号或个人数据之类的敏感信息,则存在信息在使用后可能会泄露的风险,因为您的应用程序无法从计算机内存中删除数据.
但是,对于 GUI 应用程序(例如,ssh 客户端),SecureString
必须从 System.String.所有文本控件使用字符串作为其基础数据类型.
However, in the case of a GUI application (for example, an ssh client), the SecureString
has to be built from a System.String
. All of the text controls use a string as its underlying data type.
因此,这意味着每次用户按下一个键时,旧字符串都会被丢弃,并且会构建一个新字符串来表示文本框内的值,即使使用密码掩码也是如此.而且我们无法控制何时或是否从内存中丢弃这些值中的任何一个.
So, this means that every time the user presses a key, the old string that was there is discarded, and a new string is built to represent what the value inside the text box is, even if using a password mask. And we can't control when or if any of those values are discarded from memory.
现在是登录服务器的时候了.你猜怎么着?您需要通过连接传递一个字符串以进行身份验证.因此,让我们将 SecureString
转换为 System.String
.... 现在我们在堆上有一个字符串,无法强制它通过垃圾收集(或将 0 写入其缓冲区).
Now it's time to log in to the server. Guess what? You need to pass a string over the connection for authentication. So let's convert our SecureString
into a System.String
.... and now we have a string on the heap with no way to force it to go through garbage collection (or write 0's to its buffer).
我的观点是:无论你做什么,沿着这条线的某个地方,SecureString
会被转换成System.String
,意味着它至少会在某个时刻存在于堆上(不保证垃圾回收).
My point is: no matter what you do, somewhere along the line, that SecureString
is going to be converted into a System.String
, meaning it will at least exist on the heap at some point (without any guarantee of garbage collection).
我的观点不是:是否有办法绕过将字符串发送到 ssh 连接,或绕过让控件存储字符串(制作自定义控件).对于这个问题,您可以将ssh 连接"替换为登录表"、注册表"、付款表"、foods-you-would-feed-your-puppy-but-not-your-children 表",等
My point is not: whether there are ways of circumventing sending a string to an ssh connection, or circumventing having a control store a string (make a custom control). For this question, you can replace "ssh connection" with "login form", "registration form", "payment form", "foods-you-would-feed-your-puppy-but-not-your-children form", etc.
- 那么,在什么时候使用
SecureString
实际上变成实用吗? - 是否值得花费额外的开发时间来彻底根除
System.String
对象的使用? SecureString
的全部意义在于简单地减少System.String
在堆上的时间(降低其移动到物理交换文件的风险)?- 如果攻击者已经拥有进行堆检查的方法,那么他很可能 (A) 已经拥有读取按键的方法,或者 (B) 已经物理上拥有机器...那么使用
SecureString
会阻止他获取数据吗? - 这只是通过默默无闻来确保安全"吗?
- So, at what point does using a
SecureString
actually becomepractical? - Is it ever worth the extra development time to completely eradicatethe use of a
System.String
object? - Is the whole point of
SecureString
to simply reduce the amount of time aSystem.String
is on the heap (reducing its risk of moving to a physical swap file)? - If an attacker already has the means for a heap inspection, then he most likely either (A) already has the means to read keystrokes, or (B) already physically has the machine... So would using a
SecureString
prevent him from getting to the data anyways? - Is this just "security through obscurity"?
对不起,如果我把问题问得太厚了,好奇心占了上风.随时回答我的任何或所有问题(或告诉我我的假设完全错误).:)
Sorry if I'm laying the questions on too thick, curiosity just got the better of me. Feel free to answer any or all of my questions (or tell me that my assumptions are completely wrong). :)
推荐答案
SecureString
其实有非常实用的用途.
There are actually very practical uses of SecureString
.
你知道我见过多少次这样的场景吗?(答案是:很多!):
Do you know how many times I've seen such scenarios? (the answer is: many!):
- 密码意外出现在日志文件中.
- 密码显示在某处 - 一旦 GUI 确实显示了正在运行的应用程序的命令行,并且命令行由密码组成.糟糕.
- 使用内存分析器与您的同事一起分析软件.同事在记忆中看到您的密码.听起来不真实?完全没有.
- 我曾经使用过
RedGate
软件,它可以在异常情况下捕获局部变量的值",非常有用.不过,我可以想象它会不小心记录字符串密码". - 包含字符串密码的故障转储.
- A password appears in a log file accidentally.
- A password is being shown at somewhere - once a GUI did show a command line of application that was being run, and the command line consisted of password. Oops.
- Using memory profiler to profile software with your colleague. Colleague sees your password in memory. Sounds unreal? Not at all.
- I once used
RedGate
software that could capture the "value" of local variables in case of exceptions, amazingly useful. Though, I can imagine that it will log "string passwords" accidentally. - A crash dump that includes string password.
您知道如何避免所有这些问题吗?SecureString
.它通常可以确保您不会犯这样的愚蠢错误.它是如何避免的?通过确保密码在非托管内存中加密,并且只有当您对自己在做什么有 90% 的把握时才能访问真正的值.
Do you know how to avoid all these problems? SecureString
. It generally makes sure you don't make silly mistakes as such. How does it avoid it? By making sure that password is encrypted in unmanaged memory and the real value can be only accessed when you are 90% sure what you're doing.
从某种意义上说,SecureString
很容易工作:
In the sense, SecureString
works pretty easily:
1) 一切都是加密的
2) 用户调用AppendChar
3) 解密 UNMANAGED MEMORY 中的所有内容并添加字符
3) Decrypt everything in UNMANAGED MEMORY and add the character
4) 再次加密 UNMANAGED MEMORY 中的所有内容.
4) Encrypt everything again in UNMANAGED MEMORY.
如果用户可以访问您的计算机怎么办?病毒是否能够访问所有SecureStrings
?是的.你需要做的就是在内存被解密的时候把自己挂在RtlEncryptMemory
中,你会得到未加密的内存地址的位置,并把它读出.瞧!事实上,您可以制作一种病毒,它会不断扫描 SecureString
的使用情况,并记录所有使用它的活动.我并不是说这将是一件容易的事,但它是可以做到的.如您所见,一旦您的系统中存在用户/病毒,SecureString
的强大"就完全消失了.
What if the user has access to your computer? Would a virus be able to get access to all the SecureStrings
? Yes. All you need to do is hook yourself into RtlEncryptMemory
when the memory is being decrypted, you will get the location of the unencrypted memory address, and read it out. Voila! In fact, you could make a virus that will constantly scan for usage of SecureString
and log all the activities with it. I am not saying it will be an easy task, but it can be done. As you can see, the "powerfulness" of SecureString
is completely gone once there's a user/virus in your system.
您的帖子中有几点.当然,如果您在内部使用一些包含字符串密码"的 UI 控件,那么使用实际的 SecureString
就没那么有用了.尽管如此,它仍然可以防止我上面列出的一些愚蠢行为.
You have a few points in your post. Sure, if you use some of the UI controls that hold a "string password" internally, using actual SecureString
is not that useful. Though, still, it can protect against some stupidity I've listed above.
此外,正如其他人所指出的,WPF 支持通过其
Also, as others have noted, WPF supports PasswordBox which uses SecureString
internally through its SecurePassword property.
底线是;如果您有敏感数据(密码、信用卡等),请使用 SecureString
.这就是 C# 框架所遵循的.例如,NetworkCredential
类将密码存储为 SecureString
.如果你看看这个,你可以看到SecureString
的.NET框架中~80种不同的用法.
The bottom line is; if you have sensitive data(passwords, credit-cards, ..), use SecureString
. This is what C# Framework is following. For example, NetworkCredential
class stores password as SecureString
. If you look at this, you can see over ~80 different usages in .NET framework of SecureString
.
在很多情况下,您必须将 SecureString
转换为字符串,因为某些 API 需要它.
There are many cases when you have to convert SecureString
to string, because some API expects it.
通常的问题是:
- API 是通用的.它不知道有敏感数据.
- API 知道它正在处理敏感数据并使用字符串" - 这只是糟糕的设计.
您提出了很好的观点:当SecureString
转换为string
时会发生什么?这只能因为第一点而发生.例如.API 不知道它是敏感数据.我个人没有看到这种情况发生.从 SecureString 中取出字符串并不是那么简单.
You raised good point: what happens when SecureString
is converted to string
? This can only happen because of the first point. E.g. the API does not know that it's sensitive data. I have personally not seen that happening. Getting string out of SecureString is not that simple.
原因不简单;从来没有打算让用户将 SecureString 转换为字符串,正如您所说:GC 会启动.如果您看到自己这样做,您需要退后一步问问自己:我为什么要这样做,或者我真的需要这,为什么?
It's not simple for a simple reason; it was never intended to let the user convert SecureString to string, as you stated: GC will kick in. If you see yourself doing that, you need to step back and ask yourself: Why am I even doing this, or do I really need this, why?
我看到了一个有趣的案例.即 WinApi 函数 LogonUser 以 LPTSTR 作为密码,这意味着您需要调用 SecureStringToGlobalAllocUnicode
.这基本上为您提供了位于非托管内存中的未加密密码.你需要在完成后立即摆脱它:
There's one interesting case I saw. Namely, the WinApi function LogonUser takes LPTSTR as a password, which means you need to call SecureStringToGlobalAllocUnicode
. That basically gives you unencrypted password that lives in unmanaged memory. You need to get rid of that as soon as you're done:
// 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)
).
You can always extend the SecureString
class with an extension method, such as ToEncryptedString(__SERVER__PUBLIC_KEY)
, which gives you a string
instance of SecureString
that is encrypted using server's public key. Only server can then decrypt it. Problem solved: Garbage Collection will never see the "original" string, as you never expose it in managed memory. This is exactly what is being done in PSRemotingCryptoHelper
(EncryptSecureStringCore(SecureString secureString)
).
还有一些非常相关的东西:Mono SecureString 根本不加密.该实现已被注释掉,因为..等待它.. "它以某种方式导致 nunit 测试损坏",这就是我的最后一点:
And as something very almost-related: Mono SecureString does not encrypt at all. The implementation has been commented out because ..wait for it.. "It somehow causes nunit test breakage", which brings to my last point:
SecureString
并非所有地方都支持.如果平台/架构不支持 SecureString
,您将收到异常.文档中提供了支持的平台列表.
SecureString
is not supported in everywhere. If the platform/architecture does not support SecureString
, you'll get an exception. There's a list of platforms that are supported in the documentation.
这篇关于SecureString 在 C# 应用程序中实用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!