简体   繁体   English

SecureString 在 C# 应用程序中实用吗?

[英]Is SecureString ever practical in a C# application?

Feel free to correct me if my assumptions are wrong here, but let me explain why I'm asking.如果我的假设在这里有误,请随时纠正我,但让我解释一下我为什么要问。

Taken from MSDN, a SecureString :取自 MSDN,一个SecureString

Represents text that should be kept confidential.表示应该保密的文本。 The text is encrypted for privacy when being used, and deleted from computer memory when no longer needed.文本在使用时加密以保护隐私,并在不再需要时从计算机内存中删除。

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 :我明白了,通过System.String将密码或其他私人信息存储在SecureString是完全有意义的,因为您可以控制它实际存储在内存中的方式和时间,因为System.String

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.因此,如果 String 对象包含诸如密码、信用卡号或个人数据之类的敏感信息,则存在信息在使用后可能会泄露的风险,因为您的应用程序无法从计算机内存中删除数据。

However, in the case of a GUI application (for example, an ssh client), the SecureString has to be built from a System.String .但是,对于 GUI 应用程序(例如,ssh 客户端),必须从System.String构建SecureString 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 .而且我们无法控制何时或是否从内存中丢弃这些值中的任何一个

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 .... 现在我们在堆上有一个字符串,无法强制它进行垃圾收集(或将 0 写入其缓冲区)。

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).我的观点是:无论你做什么,沿着这条线的某个地方, SecureString被转换成System.String ,这意味着它至少会在某个时候存在于堆上(没有任何垃圾收集保证)。

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).我的观点不是:是否有办法绕过将字符串发送到 ssh 连接,或者绕过让控件存储字符串(制作自定义控件)。 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.对于这个问题,您可以将“ssh 连接”替换为“登录表”、“注册表”、“付款表”、“foods-you-would-feed-your-puppy-but-not-your-children 表”,等等。

  • So, at what point does using a SecureString actually become practical?那么,在什么时候使用SecureString真正变得实用呢?
  • Is it ever worth the extra development time to completely eradicate the use of a System.String object?是否值得花费额外的开发时间来完全消除System.String对象的使用?
  • Is the whole point of SecureString to simply reduce the amount of time a System.String is on the heap (reducing its risk of moving to a physical swap file)? SecureString的全部意义在于简单地减少System.String在堆上的时间(降低其移动到物理交换文件的风险)?
  • 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?如果攻击者已经拥有进行堆检查的手段,那么他很可能 (A) 已经拥有读取击键的手段,或者 (B) 已经物理上拥有机器......所以使用SecureString阻止他获得数据呢?
  • 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).随时回答我的任何或所有问题(或告诉我我的假设完全错误)。 :) :)

There are actually very practical uses of SecureString . SecureString实际上有非常实际的用途。

Do you know how many times I've seen such scenarios?你知道我见过多少次这样的场景吗? (the answer is: many!): (答案是:很多!):

  • 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.在某处显示密码 - 一旦 GUI 确实显示了正在运行的应用程序的命令行,并且命令行由密码组成。 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.我曾经使用过RedGate软件,它可以在异常情况下捕获局部变量的“值”,非常有用。 Though, I can imagine that it will log "string passwords" accidentally.不过,我可以想象它会意外地记录“字符串密码”。
  • A crash dump that includes string password.包含字符串密码的故障转储。

Do you know how to avoid all these problems?你知道如何避免所有这些问题吗? SecureString . 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.通过确保密码在非托管内存中加密,并且只有当您对自己在做什么有 90% 的把握时才能访问真正的值。

In the sense, SecureString works pretty easily:从某种意义上说, SecureString很容易工作:

1) Everything is encrypted 1)一切都是加密的

2) User calls AppendChar 2)用户调用AppendChar

3) Decrypt everything in UNMANAGED MEMORY and add the character 3)解密UNMANAGED MEMORY中的所有内容并添加字符

4) Encrypt everything again in UNMANAGED MEMORY. 4) 再次加密 UNMANAGED MEMORY 中的所有内容。

What if the user has access to your computer?如果用户可以访问您的计算机怎么办? Would a virus be able to get access to all the SecureStrings ?病毒是否能够访问所有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.你需要做的就是在内存被解密的时候把自己挂在RtlEncryptMemory ,你会得到未加密的内存地址的位置,并读出它。 Voila!瞧! In fact, you could make a virus that will constantly scan for usage of SecureString and log all the activities with it.事实上,您可以制作一种病毒,它会不断扫描SecureString使用情况并记录所有使用它的活动。 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.如您所见,一旦您的系统中存在用户/病毒, 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.当然,如果您使用一些在内部保存“字符串密码”的 UI 控件,那么使用实际的SecureString就没有那么有用了。 Though, still, it can protect against some stupidity I've listed above.尽管如此,它仍然可以防止我上面列出的一些愚蠢行为。

Also, as others have noted, WPF supports PasswordBox which uses SecureString internally through its SecurePassword property.此外,正如其他人所指出的,WPF 支持 PasswordBox,它通过其SecurePassword属性在内部使用SecureString

The bottom line is;底线是; if you have sensitive data(passwords, credit-cards, ..), use SecureString .如果您有敏感数据(密码、信用卡等),请使用SecureString This is what C# Framework is following.这就是 C# 框架所遵循的。 For example, NetworkCredential class stores password as SecureString .例如, NetworkCredential类将密码存储为SecureString If you look at this , you can see over ~80 different usages in .NET framework of SecureString .如果你看看这个,你会发现SecureString .NET 框架中有超过 80 多种不同的用法。

There are many cases when you have to convert SecureString to string, because some API expects it.在很多情况下,您必须将SecureString转换为字符串,因为某些 API 需要它。

The usual problem is either:通常的问题是:

  1. The API is GENERIC. API 是通用的。 It does not know that there's a sensitive data.它不知道有敏感数据。
  2. The API knows that it's dealing with sensitive data and uses "string" - that's just bad design. API 知道它正在处理敏感数据并使用“字符串”——这只是糟糕的设计。

You raised good point: what happens when SecureString is converted to string ?您提出了一个好观点:当SecureString转换为string时会发生什么? This can only happen because of the first point.这只能因为第一点而发生。 Eg the API does not know that it's sensitive data.例如,API 不知道它是敏感数据。 I have personally not seen that happening.我个人没有看到这种情况发生。 Getting string out of SecureString is not that simple.从 SecureString 中取出字符串并不是那么简单。

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?从来没有打算让用户将 SecureString 转换为字符串,正如您所说:GC 将启动。如果您看到自己这样做,您需要退后一步问问自己:我为什么要这样做,或者我真的需要这,为什么?

There's one interesting case I saw.我看到了一个有趣的案例。 Namely, the WinApi function LogonUser takes LPTSTR as a password, which means you need to call SecureStringToGlobalAllocUnicode .即,WinApi 函数 LogonUser 以 LPTSTR 作为密码,这意味着您需要调用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);
}

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.您始终可以使用扩展方法扩展SecureString类,例如ToEncryptedString(__SERVER__PUBLIC_KEY) ,它为您提供使用服务器公钥加密的SecureStringstring实例。 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) ).这正是PSRemotingCryptoHelper ( EncryptSecureStringCore(SecureString secureString) ) 中EncryptSecureStringCore(SecureString secureString)

And as something very almost-related: Mono SecureString does not encrypt at all .作为一些非常相关的东西: Mono SecureString 根本不加密 The implementation has been commented out because ..wait for it.. "It somehow causes nunit test breakage" , which brings to my last point:该实现已被注释掉,因为..等待它.. “它以某种方式导致 nunit 测试损坏” ,这带来了我的最后一点:

SecureString is not supported in everywhere.并非所有地方都支持SecureString If the platform/architecture does not support SecureString , you'll get an exception.如果平台/架构不支持SecureString ,您将收到异常。 There's a list of platforms that are supported in the documentation.文档中提供了支持的平台列表。

The are few issue in your assumptions.您的假设中很少有问题。

First of all the SecureString class does not have a String constructor.首先, SecureString 类没有 String 构造函数。 In order to create one you allocate an object and then append the chars.为了创建一个对象,您分配一个对象,然后附加字符。

In the case of a GUI or a console, you can very easily pass each pressed key to a secure string.在 GUI 或控制台的情况下,您可以非常轻松地将每个按下的键传递给安全字符串。

The class is designed in a way that you cannot, by mistake, access the value that is stored.该类的设计方式使您不能错误地访问存储的值。 This means that you can not obtain the string as a password directly from it.这意味着您无法直接从中获取string作为密码。

So for using it, for example, to authenticate through the web, you will have to use proper classes that are also secure.因此,例如,要使用它来通过 Web 进行身份验证,您必须使用同样安全的适当类。

In the .NET framework you have a few classes that can use SecureString在 .NET 框架中,您有几个可以使用 SecureString 的类

  • WPF's PasswordBox control keeps the password as a SecureString internally. WPF 的 PasswordBox 控件在内部将密码保存为 SecureString。
  • System.Diagnostics.ProcessInfo's Password property is a SecureString. System.Diagnostics.ProcessInfo 的 Password 属性是 SecureString。
  • The constructor for X509Certificate2 takes a SecureString for the password. X509Certificate2 的构造函数采用 SecureString 作为密码。

(more) (更多的)

To conclude, the SecureString class can be useful, but requires more attention from the developer.总而言之,SecureString 类可能很有用,但需要开发人员给予更多关注。

All this, with examples, is well described in MSDN's documentation of SecureString所有这一切,以及示例,在 MSDN 的SecureString文档中都有很好的描述

A SecureString is useful if: SecureString 在以下情况下很有用:

  • You build it character by character (eg from console input) or obtain it from an unmanaged API您逐个构建它(例如从控制台输入)或从非托管 API 获取它

  • You use it by passing it to an unmanaged API (SecureStringToBSTR).您可以通过将其传递给非托管 API (SecureStringToBSTR) 来使用它。

If you ever convert it to a managed string, you've defeated its purpose.如果您曾经将其转换为托管字符串,那么您就违背了它的目的。

UPDATE in response to comment更新以回应评论

... or BSTR like you mention, which doesn't seem any more secure ... 或者像你提到的 BSTR,这似乎不再安全

After its been converted to a BSTR, the unmanaged component that consumes the BSTR can zero the memory.将其转换为 BSTR 后,使用 BSTR 的非托管组件可以将内存归零。 Unmanaged memory is more secure in the sense that it can be reset in this way.非托管内存更安全,因为它可以通过这种方式重置。

However, there are very few, APIs in the .NET Framework that support SecureString, so you're right to say that it's of very limited value today.但是,.NET Framework 中支持 SecureString 的 API 很少,所以您说它在今天的价值非常有限是正确的。

The main use case I would see is in a client application that requires the user to enter a highly sensitive code or password.我看到的主要用例是在要求用户输入高度敏感的代码或密码的客户端应用程序中。 The user input could be used character by character to build a SecureString, then this could be passed to an unmanaged API, which zeroes the BSTR it receives after using it.可以逐个字符地使用用户输入来构建 SecureString,然后可以将其传递给非托管 API,该 API 在使用后将收到的 BSTR 归零。 Any subsequent memory dump will not contain the sensitive string.任何后续内存转储都不会包含敏感字符串。

In a server application it's hard to see where it would be useful.在服务器应用程序中,很难看出它在哪里有用。

UPDATE 2更新 2

One example of a .NET API that accepts a SecureString isthis constructor for the X509Certificate class .接受 SecureString 的 .NET API 的一个示例是X509Certificate 类的构造函数 If you spelunk with ILSpy or similar, you'll see that the SecureString is internally converted to an unmanaged buffer ( Marshal.SecureStringToGlobalAllocUnicode ), which is then zeroed when finished with ( Marshal.ZeroFreeGlobalAllocUnicode ).如果您使用 ILSpy 或类似方法进行探索,您会看到 SecureString 在内部转换为非托管缓冲区 ( Marshal.SecureStringToGlobalAllocUnicode ),然后在完成 ( Marshal.ZeroFreeGlobalAllocUnicode ) 后将其归零。

Microsoft does not recommend to use SecureString for newer codes. Microsoft 不建议对较新的代码使用SecureString

From documentation of SecureString Class :SecureString Class 的文档中:

Important重要的

We don't recommend that you use the SecureString class for new development.我们不建议您将SecureString类用于新开发。 For more information, see SecureString shouldn't be used有关更多信息,请参阅不应使用SecureString

Which recommends:其中推荐:

Don't use SecureString for new code.不要将SecureString用于新代码。 When porting code to .NET Core, consider that the contents of the array are not encrypted in memory.将代码移植到 .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 身份验证。 on GitHub.在 GitHub 上。

As you have correctly identified, SecureString offers one specific advantage over string : deterministic erasure.正如您已经正确识别的那样, SecureString提供了一个优于string特定优势:确定性擦除。 There are two problems with this fact:这个事实有两个问题:

  1. As others have mentioned and as you have noticed by yourself, this isn't enough by itself.正如其他人所提到的以及您自己所注意到的,这本身是不够的。 You have to make sure that every step of the process (including retrieval of input, construction of the string, usage, deletion, transportation, etc) happens without defeating the purpose of using SecureString .您必须确保过程的每一步(包括检索输入、构造字符串、使用、删除、传输等)都不会违背使用SecureString的目的。 This means that you must be careful to never create a GC-managed immutable string or any other buffer that will store the sensitive information (or you'll have to keep track of that as well).这意味着您必须小心,永远不要创建 GC 管理的不可变string或任何其他将存储敏感信息的缓冲区(或者您也必须跟踪)。 In practice, this isn't always easy to achieve, because lots of APIs only offer a way to work with string , not SecureString .在实践中,这并不总是容易实现,因为许多 API 只提供一种处理string ,而不是SecureString And even if you do manage to everything right...即使你确实做到了一切正确......
  2. SecureString protects against very specific kinds of attack (and for some of them, it's not even that reliable). SecureString可以防止非常特定类型的攻击(对于其中一些攻击,它甚至不那么可靠)。 For example, SecureString does allow you to shrink the time window in which an attacker can dump the memory of your process and successfully extract the sensitive information (again, as you correctly pointed out), but hoping that the window is too small for the attacker to take a snapshot of your memory isn't considered security at all.例如, SecureString确实允许您缩小攻击者可以转储进程内存并成功提取敏感信息的时间窗口(同样,正如您正确指出的那样),但希望窗口对于攻击者来说太小对您的记忆进行快照根本不被认为是安全的。

So, when should you use it?那么,你应该什么时候使用它? Only when you're working with something that can allow you to work with SecureString for all your needs and even then you should still be mindful that this is secure only in specific circumstances.只有当您使用的东西可以让您使用SecureString满足您的所有需求时,您仍然应该注意这仅在特定情况下是安全的。

Below text is copied from HP Fortify static code analyzer以下文字复制自 HP Fortify 静态代码分析器

Abstract: The method PassString() in PassGenerator.cs stores sensitive data in an insecure manner (ie in string), making it possible to extract the data via inspecting the heap.摘要: PassGenerator.cs 中的 PassString() 方法以不安全的方式(即以字符串形式)存储敏感数据,从而可以通过检查堆来提取数据。

Explanation: Sensitive data (such as passwords, social security numbers, credit card numbers etc.) stored in memory can be leaked if it is stored in a managed String object.说明:如果将存储在内存中的敏感数据(例如密码、社会保险号、信用卡号等)存储在托管 String 对象中,则可能会泄漏。 String objects are not pinned, so the garbage collector can relocate these objects at will and leave several copies in memory.字符串对象没有被固定,因此垃圾收集器可以随意重新定位这些对象并在内存中保留多个副本。 These objects are not encrypted by default, so anyone who can read the process' memory will be able to see the contents.这些对象默认不加密,因此任何可以读取进程内存的人都可以看到内容。 Furthermore, if the process' memory gets swapped out to disk, the unencrypted contents of the string will be written to a swap file.此外,如果进程的内存被换出到磁盘,字符串的未加密内容将被写入交换文件。 Lastly, since String objects are immutable, removing the value of a String from memory can only be done by the CLR garbage collector.最后,由于 String 对象是不可变的,从内存中删除 String 的值只能由 CLR 垃圾收集器完成。 The garbage collector is not required to run unless the CLR is low on memory, so there is no guarantee as to when garbage collection will take place.除非 CLR 内存不足,否则不需要运行垃圾收集器,因此无法保证垃圾收集何时发生。 In the event of an application crash, a memory dump of the application might reveal sensitive data.在应用程序崩溃的情况下,应用程序的内存转储可能会泄露敏感数据。

Recommendations: Instead of storing sensitive data in objects like Strings, store them in a SecureString object.建议:不要将敏感数据存储在像字符串这样的对象中,而是将它们存储在 SecureString 对象中。 Each object stores its contents in an encrypted format in memory at all times.每个对象始终以加密格式将其内容存储在内存中。

I'd like to address this point:我想说明这一点:

If an attacker already has the means for a heap inspection, then they most likely either (A) already have the means to read keystrokes, or (B) already physically have the machine ... So would using a SecureString prevent them from getting to the data anyways?如果攻击者已经拥有进行堆检查的方法,那么他们很可能 (A) 已经拥有读取击键的方法,或者 (B) 已经物理上拥有机器......那么使用SecureString阻止他们进入数据呢?

An attacker may not have full access to the computer and the application but can have means to access some parts of the memory of the process.攻击者可能无法完全访问计算机和应用程序,但可以访问进程内存的某些部分。 It is usually caused by bugs like buffer overruns when specially constructed input can cause the application to expose or overwrite some part of the memory.当特殊构造的输入可能导致应用程序暴露或覆盖内存的某些部分时,它通常是由缓冲区溢出等错误引起的。

HeartBleed leaking memory HeartBleed 内存泄漏

Take Heartbleed for example.以 Heartbleed 为例。 Specially constructed requests can cause the code to expose random parts of the memory of the process to the attacker.特殊构造的请求可能会导致代码向攻击者公开进程内存的随机部分。 An attacker can extract SSL certificates from the memory, yet the only thing he needs is just to use a malformed request.攻击者可以从内存中提取 SSL 证书,但他唯一需要的只是使用格式错误的请求。

In the world of the managed code, buffer overruns become a problem much less often.在托管代码的世界中,缓冲区溢出成为问题的频率要低得多。 And in case of WinForms, data is already stored in an insecure manner and you can't do anything about it.对于 WinForms,数据已经以不安全的方式存储,您对此无能为力。 This renders the protection with SecureString pretty much useless.这使得SecureString的保护几乎毫无用处。

However, GUI can be programmed to use SecureString , and in such case reducing the window of password availability in the memory can be worth it.但是,可以对 GUI 进行编程以使用SecureString ,在这种情况下,减少内存中的密码可用性窗口是值得的。 For example, PasswordBox.SecurePassword from WPF is of type SecureString .例如,WPF 中的PasswordBox.SecurePasswordSecureString类型。

Some time ago I had to create ac# interface against a java credit card payment gateway and one needed a compatible secure communications key encryptions.前段时间我不得不针对 java 信用卡支付网关创建 ac# 接口,并且需要一个兼容的安全通信密钥加密。 As the Java implementation was rather specific and I had to work with the protected data in a given way.由于 Java 实现相当具体,我必须以给定的方式处理受保护的数据。

I found this design to be quite easy to use and easyer than working with SecureString… for those that like to use… feel free, no legal restrictions :-).我发现这种设计非常易于使用,而且比使用 SecureString 更容易……对于那些喜欢使用的人……随意,没有法律限制:-)。 Note that these classes are internal, you might need to make them public.请注意,这些类是内部类,您可能需要将它们设为公开。

namespace Cardinity.Infrastructure
{
    using System.Security.Cryptography;
    using System;
    enum EncryptionMethods
    {
        None=0,
        HMACSHA1,
        HMACSHA256,
        HMACSHA384,
        HMACSHA512,
        HMACMD5
    }


internal class Protected
{
    private  Byte[] salt = Guid.NewGuid().ToByteArray();

    protected byte[] Protect(byte[] data)
    {
        try
        {
            return ProtectedData.Protect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }

    protected byte[] Unprotect(byte[] data)
    {
        try
        {
            return ProtectedData.Unprotect(data, salt, DataProtectionScope.CurrentUser);
        }
        catch (CryptographicException)//no reason for hackers to know it failed
        {
#if DEBUG
            throw;
#else
            return null;
#endif
        }
    }
}


    internal class SecretKeySpec:Protected,IDisposable
    {
        readonly EncryptionMethods _method;

        private byte[] _secretKey;
        public SecretKeySpec(byte[] secretKey, EncryptionMethods encryptionMethod)
        {
            _secretKey = Protect(secretKey);
            _method = encryptionMethod;
        }

        public EncryptionMethods Method => _method;
        public byte[] SecretKey => Unprotect( _secretKey);

        public void Dispose()
        {
            if (_secretKey == null)
                return;
            //overwrite array memory
            for (int i = 0; i < _secretKey.Length; i++)
            {
                _secretKey[i] = 0;
            }

            //set-null
            _secretKey = null;
        }
        ~SecretKeySpec()
        {
            Dispose();
        }
    }

    internal class Mac : Protected,IDisposable
    {
        byte[] rawHmac;
        HMAC mac;
        public Mac(SecretKeySpec key, string data)
        {

            switch (key.Method)
            {
                case EncryptionMethods.HMACMD5:
                    mac = new HMACMD5(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA512:
                    mac = new HMACSHA512(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA384:
                    mac = new HMACSHA384(key.SecretKey);
                    break;
                case EncryptionMethods.HMACSHA256:
                    mac = new HMACSHA256(key.SecretKey);

                break;
                case EncryptionMethods.HMACSHA1:
                    mac = new HMACSHA1(key.SecretKey);
                    break;

                default:                    
                    throw new NotSupportedException("not supported HMAC");
            }
            rawHmac = Protect( mac.ComputeHash(Cardinity.ENCODING.GetBytes(data)));            

        }

        public string AsBase64()
        {
            return System.Convert.ToBase64String(Unprotect(rawHmac));
        }

        public void Dispose()
        {
            if (rawHmac != null)
            {
                //overwrite memory address
                for (int i = 0; i < rawHmac.Length; i++)
                {
                    rawHmac[i] = 0;
                }

                //release memory now
                rawHmac = null;

            }
            mac?.Dispose();
            mac = null;

        }
        ~Mac()
        {
            Dispose();
        }
    }
}

Feel free to correct me if my assumptions are wrong here, but let me explain why I'm asking.如果我的假设在这里错误,请随时纠正我,但是让我解释为什么我要问。

Taken from MSDN, a SecureString :取自MSDN,一个SecureString

Represents text that should be kept confidential.表示应保密的文本。 The text is encrypted for privacy when being used, and deleted from computer memory when no longer needed.文本在使用时被加密以保护隐私,并在不再需要时从计算机内存中删除。

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 :我得到这个,它使完整意义上存储的密码或其他私人信息SecureStringSystem.String ,因为您可以控制如何以及何时它实际上是存储在内存中,因为System.String

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.因此,如果String对象包含敏感信息,例如密码,信用卡号或个人数据,则使用该信息后可能会泄露该信息,因为您的应用程序无法从计算机内存中删除数据。

However, in the case of a GUI application (for example, an ssh client), the SecureString has to be built from a System.String .但是,对于GUI应用程序(例如ssh客户端),必须从System.String构建SecureString 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 .而且我们无法控制何时或是否从内存中丢弃这些值中的任何一个

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 ...。现在堆上有一个字符串,无法强制其通过垃圾回收(或将0写入其缓冲区)。

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

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

  • So, at what point does using a SecureString actually become practical?那么,在什么时候使用SecureString实际上变得可行呢?
  • Is it ever worth the extra development time to completely eradicate the use of a System.String object?完全根除System.String对象的使用是否值得花费额外的开发时间?
  • Is the whole point of SecureString to simply reduce the amount of time a System.String is on the heap (reducing its risk of moving to a physical swap file)? SecureString的全部目的是简单地减少System.String在堆上的时间(减少其移至物理交换文件的风险)吗?
  • 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?如果攻击者已经具有检查堆的方法,那么他很可能要么(A)已经具有读取击键的方法,要么(B)已经物理拥有了机器……因此使用SecureString阻止他进入反正数据呢?
  • 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).随时回答我的任何或所有问题(或告诉我我的假设完全错误)。 :) :)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM