如何在 C# 中正确处理密码

信息安全 。网 记忆 bcrypt 细绳
2021-09-02 14:02:19

众所周知,C#string非常不安全,它没有固定在 RAM 中,垃圾收集器可以移动它、复制它、在 RAM 中留下它的多个痕迹,并且可以交换 RAM 并作为要读取的文件使用,更不用说其他几个已知的事实了。

为了缓解这种情况,微软想出了SecureString. 问题是:使用它的正确方法是什么?

我问这个是因为迟早你将不得不提取内部字符串。是的,SecureString确实允许我们一次写一个char,并在隐藏它的秘密的同时给我们一些其他的小用法,但有时我们需要一次整个字符串或类似的东西。到目前为止,我看到的所有实现string最后都使用了一个常规,从其中提取和解密的内容SecureString我认为(或希望)在某些情况下是可以避免的,即使它可能涉及一些复杂的代码来绕过 .NET string

就我而言,我使用了一个名为BCrypt 的密码散列库它使用正则string来计算哈希,我认为这远非理想。我没有找到任何可以SecureString直接接受 a 的库,所以我没有太多选择,只能使用它。

目前我运行这样的代码:

SecureString password; // This usually comes from a PasswordBox Property
IntPtr passwordBSTR = Marshal.SecureStringToBSTR(password);

string insecurePassword = Marshal.PtrToStringBSTR(passwordBSTR);
Marshal.ZeroFreeBSTR(passwordBSTR);
passwordBSTR = default(IntPtr);
password.Clear();

// Use the insecurePassword....usually as:
bool result = BCrypt.Net.BCrypt.Verify(insecurePassword, user.Password); // This hashes the provided password and compares it with another BCrypt hash.

insecureString = string.Empty; // hoping for the GC to act now, fingers crossed.

// use result and etc...

这样,该方法的原始密码string将被复制多次,BCrypt并且可能在其中复制更多次。

这让我想到了一些问题:

1 -在内存管理的意义上,字符串是否BSTR像字符串?C它们是否固定在 RAM 中,我可以按照我认为合适的方式操作和擦除它们,这样在 C# 中使用它们或与 C++ 代码互操作会更安全,这样我就可以消除很多我的 .NETstring操作(比如验证是否密码字符串为空或空格或具有正确的长度或具有所需的密码强度)?

2 - 如果问题的 #1 答案是“是”,那么我是否应该花一些时间将 BSTR 字符串传递给互操作 C++ 代码来计算它的哈希值并进行验证?

3 - 我后来的代码是正确的还是我在SecureString操作上遗漏了一些东西?

明确一点:我对这个问题的主要目的是评估是否应该将我的所有密码处理代码从我的 C#(可能使用 C++/CLI 或互操作)传递给 C++,以实现比 C# 提供的更安全的代码。这种情况真的让我担心密码字符串散列是多个字符串将被创建和复制,用户的秘密信息分散在整个 RAM 中。

举个例子:我可以做一个像这样的方法:

public string SecureHash(SecureString password)

这个方法将提取SecureString值并将其作为BSTR字符串(或其他提取选项,老实说我不知道​​它们之间的好处)传递给 C++ 散列方法,因此我们可以控制密码在内存中的所有位置。

我要求这不要进行大量工作来重构我的代码,最后发现这一切都是徒劳的,因为我一直在SecureStrings错误地使用并且已经有解决方案,或者有时会发生错误的安全感,因为我不知道BSTR字符串是如何在内存或其他SecureStrings 提取选项(Unicode、ANSI 等)中实现的。

1个回答

这一切都取决于你想做什么。

回答您帖子的第一部分:

以下是安全字符串的 .net 实现: WPF 的System.Windows.Controls.PasswordBox控件在内部将密码保存为 SecureString(通过 PasswordBox::SecurePassword 作为副本公开)System.Diagnostics.ProcessStartInfo::Password属性为一个 SecureString ,最后X509Certificate2接受一个 SecureString 作为密码。

回答您帖子的第二部分:

如果您想深入了解散列的内部结构,BCrypt/BCryptPrimatives/PBKDF2 通常被认为符合 FIPS 140-2(政府标准),特别是因为它们会仔细处理磁盘和内存中的散列和密码。

如果您真的想深入了解细节(而且看起来您确实这样做了......)请访问此处:http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp1892。 pdf

在那个 PDF 中有很多关于它是如何工作的细节,很抱歉这个链接,它只是一个太大的主题,无法在一篇文章中涵盖。