SecureString
в .NET хранит символы в зашифрованном виде, однако доступ к открытому тексту можно получить с помощью Marshal.SecureStringToBSTR
, который расшифровывает буфер в неуправляемый буфер, а затем можно проверить строку. В соответствии с передовой практикой этот буфер должен быть очищен с использованием Marshal.ZeroFreeBSTR
, чтобы гарантировать отсутствие в памяти символов экземпляра SecureString
, например, приведенный ниже код безопасно выполняет итерацию по каждому символу в SecureString
таким образом, чтобы предотвратить вся строка копируется в управляемую память:
SecureString ss = ...
IntPtr bstr = Marshal.SecureStringToBSTR( ss );
if( bstr != IntPtr.Zero )
{
try
{
Int32 length = Marshal.ReadInt32( bstr, -4 ) / 2; // a BSTR length is stored in the 4 bytes immediately before the first character of the string.
for( Int32 i = 0; i < length; i++ )
{
Char c = (Char)Marshal.ReadInt16( bstr, i * 2 );
Console.Write( c );
}
}
finally
{
Marshal.ZeroFreeBSTR( bstr );
bstr = IntPtr.Zero;
}
}
Макет памяти BSTR имеет 4-байтовый префикс длины непосредственно перед первым символом строки, однако я заметил, что хотя Marshal.ZeroFreeBSTR
обнуляет символы строки, он не обнуляет длину BSTR префикс - это означает, что существует вероятность раскрытия информации (в частности, длины пароля), если другой процесс отслеживает память моего процесса.
Я мог бы обойти это, вручную обнулив его:
finally
{
Marshal.ZeroFreeBSTR( bstr );
Marshal.WriteInt16( bstr, -4, 0 );
bstr = IntPtr.Zero;
}
... но я не знаю, есть ли причина ZeroFreeBSTR
не очищает префикс длины, возможно, он используется в другом месте для домашнего хозяйства? Это безопасно?
Вот несколько скриншотов окна Памяти в VS.
Вот буфер BSTR в памяти непосредственно перед вызовом ZeroFreeBSTR
, красная область - это поле длиной 4 байта, а синяя область - это символы wchar_t
с нулевым символом в конце:
Вот та же память сразу после возврата ZeroFreeBSTR
. Символы были обнулены, но значение длины (6 == 0x0C
) остается: