【发布时间】:2017-01-31 13:19:14
【问题描述】:
众所周知,C#string 非常不安全,它没有固定在 RAM 中,垃圾收集器可以移动它、复制它、在 RAM 中留下它的多个痕迹,并且 RAM 可以交换并用作一个要读取的文件,没有提及其他几个已知事实。
为了缓解这种情况,Microsoft 提出了SecureString。问题是:正确的使用方法是什么?
我问这个是因为迟早你必须提取内部字符串。是的,SecureString 确实允许我们一次写一个char 并在隐藏它的秘密的同时给我们一些其他的小用法,但有时我们需要一次整个字符串或类似的东西。到目前为止,我看到的所有实现最后都使用常规的string,从SecureString 提取和解密的内容我认为(或希望)在某些情况下是可以避免的,即使它可能涉及一些复杂的代码来解决.NET string。
在我的例子中,我使用了一个名为 BCrypt 的密码哈希库。它使用常规的string 来计算哈希值,我认为这远非理想。我没有找到任何可以直接接受SecureString 的库,所以我没有太多选择,只能使用它。
目前我运行这样的代码:
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...
这样,BCrypt 方法的原始密码 string 将被复制多次,并且可能在其中复制更多次。
这让我想到了一些问题:
1 - 在内存管理的意义上,BSTR 字符串是否类似于 C 字符串? 它们是否固定在 RAM 中,我可以按照我认为合适的方式操作和擦除它们,所以它将是在 C# 中使用它们或与 C++ 代码互操作更安全,这样我就可以消除很多我的 .NET string 操作(例如验证密码字符串是否为空或空格或具有正确的长度或具有所需的密码强度)?
2 - 如果问题的 #1 答案是“是”,那么我是否应该花一些时间将 BSTR 字符串传递给互操作 C++ 代码以计算其哈希值并进行验证?
3 - 我后来的代码是正确的还是我在SecureString 操作上遗漏了什么?
明确一点:我对这个问题的主要目的是评估我是否应该将所有密码处理代码从我的 C# 传递给 C++(可能使用 C++/CLI 或互操作)以实现比 C# 提供的代码更安全。这种情况真的让我担心密码字符串散列,因为将创建多个字符串并将其复制,用户的秘密信息散布在整个 RAM 中。
举个例子:我可以做一个类似的方法:
public string SecureHash(SecureString password)
而且这个方法会提取SecureString值并将它作为BSTR字符串(或其他提取选项,老实说我不知道它们之间的好处)传递给C++哈希方法,所以我们可以控制所有我们的密码在内存中的位置。
我要求这不要进行大量工作来重构我的代码,最后发现这一切都是徒劳的,因为我一直在错误地使用SecureStrings,并且已经有解决方案,或者是错误的安全感,因为它有时会发生,因为我不知道BSTR 字符串是如何在内存或其他SecureStrings extraction options(Unicode、ANSI 等)中实现的。
【问题讨论】:
-
您可以重写 BCrypt 以使用一个采用安全字符串的方法。根据:msdn.microsoft.com/en-us/library/… 如果您必须经常修改字符串等,实际上并不那么安全。
-
@MattClark 这正是我的想法。我的问题是值得还是不值得。我不知道 BSTR 字符串的内存行为,所以我可能会因为错误的安全感而烦恼。
-
BSTR 实际上并没有比 SecureString 获得任何好处,特别是因为 BSTR 只是非托管内存中的未加密字符串。在安全字符串和 bstr 的情况下,您可以手动对它们进行垃圾收集。
-
我不打算通过 BSTR 从 SecureString 获得任何东西。这个想法是将 SecureString 传递给将散列它的秘密值的方法。在该方法内部将只有 BSTR 操作,因为 SecureString 值将被提取到 BSTR。但是我不确定这将如何比简单的字符串更安全,因为我不知道内存中的 BSTR 行为,即使 BSTR 是最佳选择,因为还有其他选项可以解密,例如
CoTaskMemAnsi、@987654348 @、GlobalAllocAnsi和GlobalAllocUnicode。 -
如果您在 Windows 上运行,我会简单地与本地 Windows API 互操作,例如 BCryptHashData 函数,例如(有点 hacky):codereview.stackexchange.com/questions/93111/…
标签: c# c++ string security memory