众所周知,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 等)中实现的。