由于我没有足够的声望点来评论 Eric 的回答,所以我必须发表这篇文章。
在我看来,Eric 的代码存在问题,因为GCHandle.Alloc(workArray, ...)
操作不正确。它不应该固定的null
值,workArray
而是实际的数组,它将在更远的几行中创建。
此外handle.Free()
可以抛出一个InvalidOperationException
,因此我建议把它Marshal.ZeroFreeBSTR(...)
放在至少二进制字符串bstr
指向零之后。
修改后的代码是这样的:
public static T Process<T>(this SecureString src, Func<byte[], T> func)
{
IntPtr bstr = IntPtr.Zero;
byte[] workArray = null;
GCHandle? handle = null; // Change no. 1
try
{
/*** PLAINTEXT EXPOSURE BEGINS HERE ***/
bstr = Marshal.SecureStringToBSTR(src);
unsafe
{
byte* bstrBytes = (byte*)bstr;
workArray = new byte[src.Length * 2];
handle = GCHandle.Alloc(workArray, GCHandleType.Pinned); // Change no. 2
for (int i = 0; i < workArray.Length; i++)
workArray[i] = *bstrBytes++;
}
return func(workArray);
}
finally
{
if (workArray != null)
for (int i = 0; i < workArray.Length; i++)
workArray[i] = 0;
if (bstr != IntPtr.Zero)
Marshal.ZeroFreeBSTR(bstr);
handle?.Free(); // Change no. 3 (Edit: no try-catch but after Marshal.ZeroFreeBSTR)
/*** PLAINTEXT EXPOSURE ENDS HERE ***/
}
}
这些修改确保正确的byte
Array 固定在内存中(更改 1 和 2)。此外,他们避免将未加密的二进制字符串仍然加载到内存中,以防handle?.Free()
引发异常(更改号 3)。