Короткий ответ
почему я не могу просто сказать:
SecureString password = new SecureString("password");
Потому что теперь у вас есть password
в памяти; без возможности стереть его - в этом и заключается смысл SecureString .
Длинный ответ
Причина SecureString заключается в том, что вы не можете использовать ZeroMemory , чтобы стереть конфиденциальные данные, когда вы закончите с ними. Он существует для решения проблемы, которая существует , поскольку из CLR.
В обычном нативном приложении вы бы позвонили SecureZeroMemory
:
Заполняет блок памяти нулями.
Примечание : SecureZeroMemory идентичен ZeroMemory
, , за исключением того, что компилятор его не оптимизирует.
Проблема в том, что вы не можете позвонить ZeroMemory
или SecureZeroMemory
внутри .NET. И в .NET строки неизменны; вы не можете даже перезаписать содержимое строки, как вы можете это сделать на других языках:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;
Так что вы можете сделать? Как мы можем предоставить в .NET возможность стереть пароль или номер кредитной карты из памяти, когда мы закончили с этим?
Единственный способ сделать это - поместить строку в некоторый собственный блок памяти, где вы можете и затем вызовете ZeroMemory
. Собственный объект памяти, такой как:
- a BSTR
- HGLOBAL
- CoTaskMem неуправляемая память
SecureString возвращает утраченную способность
В .NET строки не могут быть стерты, когда вы закончите с ними:
- они неизменны; вы не можете перезаписать их содержимое
- вы не можете
Dispose
из них
- их очистка зависит от сборщика мусора
SecureString существует как способ обойти безопасность строк и быть в состоянии гарантировать их очистку, когда вам нужно.
Вы задали вопрос:
почему я не могу просто сказать:
SecureString password = new SecureString("password");
Потому что теперь у вас есть password
в памяти; без возможности вытереть его. Он застрял там, пока CLR не решит повторно использовать эту память. Вы вернули нас туда, где мы начали; работающее приложение с паролем, от которого мы не можем избавиться, и где дамп памяти (или Process Monitor) может видеть пароль.
SecureString использует API защиты данных для хранения строки, зашифрованной в памяти; таким образом, строка не будет существовать в файлах подкачки, аварийных дампах или даже в окне локальных переменных, когда коллега просматривает ваше имя.
Как мне прочитать пароль?
Тогда возникает вопрос: как мне взаимодействовать со строкой? Вы абсолютно не хотите такой метод, как:
String connectionString = secureConnectionString.ToString()
потому что теперь вы вернулись туда, откуда начали - пароль, от которого вы не можете избавиться. Вы хотите, чтобы заставил разработчиков правильно обрабатывать чувствительную строку - чтобы она могла быть стерта из памяти.
Именно поэтому .NET предоставляет три удобные вспомогательные функции для перенаправления SecureString в неуправляемую память:
Вы преобразуете строку в неуправляемый блоб памяти, обрабатываете его и затем стираете.
Некоторые API принимают SecureStrings . Например, в ADO.net 4.5 SqlConnection.Credential принимает набор SqlCredential :
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
Вы также можете изменить пароль в строке подключения:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
И в .NET есть много мест, где они продолжают принимать простую строку в целях совместимости, а затем быстро превращают ее в SecureString.
Как поместить текст в SecureString?
Это все еще оставляет проблему:
Как мне в первую очередь получить пароль для SecureString?
Это проблема, но главное - заставить вас задуматься о безопасности.
Иногда функциональность уже предоставляется для вас. Например, элемент управления WPF PasswordBox может вернуть вам введенный пароль как SecureString напрямую:
Получает пароль, который в данный момент хранится в PasswordBox как SecureString .
Это полезно, потому что везде, где вы раньше передавали необработанную строку, у вас теперь есть система типов, жалующаяся на то, что SecureString несовместима со String. Вы хотите идти как можно дольше, прежде чем конвертировать SecureString обратно в обычную строку.
Преобразование SecureString достаточно просто:
- SecureStringToBSTR
- PtrToStringBSTR
как в:
private static string CreateString(SecureString secureString)
{
IntPtr intPtr = IntPtr.Zero;
if (secureString == null || secureString.Length == 0)
{
return string.Empty;
}
string result;
try
{
intPtr = Marshal.SecureStringToBSTR(secureString);
result = Marshal.PtrToStringBSTR(intPtr);
}
finally
{
if (intPtr != IntPtr.Zero)
{
Marshal.ZeroFreeBSTR(intPtr);
}
}
return result;
}
Они просто не хотят, чтобы вы это делали.
Но как мне получить строку в SecureString? Что вам нужно сделать, так это прекратить вводить пароль в String . Вы должны были иметь это в что-то еще. Был бы полезен даже массив Char[]
.
Вот когда вы можете добавить каждый символ и , чтобы стереть открытый текст, когда вы закончите:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
Ваш пароль должен храниться в некоторой памяти, которую вы можете стереть. Загрузите его в SecureString оттуда.
tl; dr: SecureString существует для предоставления эквивалента ZeroMemory .
Некоторые люди не видят смысла в стирании пароля пользователя из памяти, когда устройство заблокировано , или стирании нажатий клавиш из памяти после аутентификации . Эти люди не используют SecureString.