Короткий ответ
почему я не могу просто сказать:
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
. Нативный объект памяти, такой как:
- БСТР
- 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? Ну, что вам нужно сделать, это перестать иметь пароль в строке в первую очередь. Вы должны были иметь это в чем - то другом. Даже Char[]
массив будет полезен.
Вот когда вы можете добавить каждый символ и стереть открытый текст, когда вы закончите:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
Вам нужен пароль , сохраненный в некоторой памяти , которые вы можете протирать. Загрузите его в SecureString оттуда.
tl; dr: SecureString существует для предоставления эквивалента ZeroMemory .
Некоторые люди не видят смысла стирать пароль пользователя из памяти, когда устройство заблокировано , или стирать нажатия клавиш из памяти после их аутентификации . Эти люди не используют SecureString.
SecureString
для новой разработки: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md