Когда мне понадобится SecureString в.NET?
Я пытаюсь понять цель SecureString.NET. Из MSDN:
Экземпляр класса System.String является неизменным и, если он больше не нужен, не может быть программно запланирован для сборки мусора; то есть экземпляр только для чтения после его создания, и невозможно предсказать, когда экземпляр будет удален из памяти компьютера. Следовательно, если объект String содержит конфиденциальную информацию, такую как пароль, номер кредитной карты или личные данные, существует риск, что эта информация может быть раскрыта после ее использования, поскольку ваше приложение не может удалить данные из памяти компьютера.
Объект SecureString похож на объект String в том смысле, что он имеет текстовое значение. Однако значение объекта SecureString автоматически шифруется, может изменяться до тех пор, пока приложение не помечает его как доступное только для чтения, и может быть удалено из памяти компьютера вашим приложением или сборщиком мусора.NET Framework.
Значение экземпляра SecureString автоматически шифруется при инициализации экземпляра или при изменении значения. Ваше приложение может сделать экземпляр неизменным и предотвратить его дальнейшее изменение, вызвав метод MakeReadOnly.
Является ли автоматическое шифрование большой отдачей?
И почему я не могу просто сказать:
SecureString password = new SecureString("password");
вместо
SecureString pass = new SecureString();
foreach (char c in "password".ToCharArray())
pass.AppendChar(c);
Какой аспект SecureString мне не хватает?
11 ответов
Некоторые части платформы, которые в настоящее время используют SecureString
:
- WPF-х
System.Windows.Controls.PasswordBox
контроль хранит пароль внутри SecureString (предоставляется как копия черезPasswordBox::SecurePassword
) -
System.Diagnostics.ProcessStartInfo::Password
собственностьSecureString
- Конструктор для
X509Certificate2
занимаетSecureString
для пароля
Основная цель - уменьшить поверхность атаки, а не устранить ее. SecureStrings
"закреплены" в оперативной памяти, поэтому сборщик мусора не будет перемещать его или копировать. Это также гарантирует, что простой текст не будет записан в файл подкачки или в дампы ядра. Шифрование больше похоже на обфускацию и не остановит решительного хакера, который сможет найти симметричный ключ, используемый для его шифрования и дешифрования.
Как уже говорили другие, причина, по которой вы должны создать SecureString
посимвольный характер объясняется первым очевидным недостатком, заключающимся в том, чтобы поступить иначе: у вас уже есть секретное значение в виде простой строки, так какой смысл?
SecureString
Это первый шаг в решении проблемы с курицей и яйцом, поэтому, хотя большинство современных сценариев требуют преобразования их обратно в обычные строки, чтобы вообще их использовать, их существование в среде теперь означает лучшую поддержку для них в Будущее - по крайней мере, до такой степени, что ваша программа не должна быть слабым звеном.
Много хороших ответов; Вот краткий обзор того, что обсуждалось.
Microsoft внедрила класс SecureString, стремясь обеспечить лучшую безопасность с помощью конфиденциальной информации (например, кредитных карт, паролей и т. Д.). Это автоматически обеспечивает:
- шифрование (в случае дампов памяти или кэширования страниц)
- закрепление в памяти
- возможность пометить только для чтения (для предотвращения дальнейших изменений)
- безопасная конструкция, НЕ позволяющая передавать константную строку в
В настоящее время SecureString ограничен в использовании, но в будущем ожидают лучшего внедрения.
Основываясь на этой информации, конструктор SecureString не должен просто брать строку и разрезать ее до массива char, поскольку указанная строка отрицательно сказывается на цели SecureString.
Дополнительная информация:
- Сообщение из блога.NET Security, в котором говорится о том же, что и здесь.
- И еще один, возвращающийся к нему и упоминающий инструмент, который МОЖЕТ сбросить содержимое SecureString.
Изменить: мне было трудно выбрать лучший ответ, так как во многих есть хорошая информация; Жаль, что нет вариантов ответа с помощью.
Короткий ответ
почему я не могу просто сказать:
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 в неуправляемую память:
- SecureStringToBSTR (освобождается с помощью ZeroFreeCoTaskMemUnicode)
- SecureStringToCoTaskMemUnicode (освобождается с помощью ZeroFreeCoTaskMemUnicode)
- SecureStringToGlobalAllocUnicode (освобождается с помощью ZeroFreeGlobalAllocUnicode)
Вы конвертируете строку в неуправляемый блоб памяти, обрабатываете ее, а затем снова стираете.
Некоторые 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.SecurePassword Свойство
Получает пароль, который в данный момент хранится в 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 в текущей версии Framework. Это действительно полезно только для взаимодействия с неуправляемыми API - вы можете упорядочить его с помощью Marshal.SecureStringToGlobalAllocUnicode.
Как только вы конвертируете его в / из System.String, вы побеждаете его назначение.
Образец MSDN генерирует символ SecureString за раз из ввода с консоли и передает защищенную строку в неуправляемый API. Это довольно запутанно и нереально.
Можно ожидать, что в будущих версиях.NET будет больше поддержки SecureString, что сделает его более полезным, например:
SecureString Console.ReadLineSecure () или аналогичный для чтения ввода консоли в SecureString без всего извилистого кода в образце.
Замена WinForms TextBox, которая хранит свое свойство TextBox.Text в виде защищенной строки, чтобы пароли можно было вводить безопасно.
Расширения API-интерфейсов, связанных с безопасностью, позволяющие передавать пароли как SecureString.
Без вышеизложенного SecureString будет иметь ограниченную ценность.
Я полагаю, что причина, по которой вы должны выполнять добавление символов вместо одной простой реализации, заключается в том, что в фоновом режиме передача "пароля" в конструктор SecureString помещает эту строку "пароля" в память, разрушая цель безопасной строки.
Добавляя, вы помещаете в память только персонажа за раз, что, скорее всего, не означает, что они физически близки друг к другу, что значительно усложняет восстановление исходной строки. Я могу ошибаться здесь, но так мне это объяснили.
Цель этого класса - предотвратить доступ к защищенным данным с помощью дампа памяти или аналогичного инструмента.
MS обнаружила, что в некоторых случаях сбоя сервера (настольного компьютера и т. Д.) Бывали ситуации, когда среда выполнения делала дамп памяти, раскрывая содержимое содержимого памяти. Secure String шифрует ее в памяти, чтобы злоумышленник не смог получить содержимое строки.
Одним из больших преимуществ SecureString является то, что предполагается исключить возможность сохранения ваших данных на диск из-за кэширования страниц. Если у вас есть пароль в памяти, а затем загружается большая программа или набор данных, ваш пароль может быть записан в файл подкачки, поскольку ваша программа выгружается из памяти. С SecureString, по крайней мере, данные не будут бесконечно бездействовать на вашем диске в виде открытого текста.
Я бы прекратил использовать SecureString. Похоже, парни из PG отказались от поддержки. Возможно, даже потяните его в будущем - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring.
Мы должны удалить шифрование из SecureString на всех платформах в.NET Core - Мы должны устареть SecureString - Мы, вероятно, не должны выставлять SecureString в.NET Core
Ну, как говорится в описании, значение хранится в зашифрованном виде, что означает, что дамп памяти вашего процесса не будет раскрывать значение строки (без некоторой довольно серьезной работы).
Причина, по которой вы не можете просто создать SecureString из константной строки, состоит в том, что тогда у вас будет незашифрованная версия строки в памяти. Ограничение создания строки по частям снижает риск одновременного хранения всей строки в памяти.
Я предполагаю, что это потому, что строка предназначена для безопасности, то есть хакер не должен быть в состоянии прочитать ее. Если вы инициализируете его строкой, хакер может прочитать исходную строку.
Другой вариант использования - это когда вы работаете с платежными приложениями (POS), и вы просто не можете использовать неизменяемые структуры данных для хранения конфиденциальных данных, потому что вы осторожный разработчик. Например: если я буду хранить конфиденциальные данные карты или метаданные авторизации в неизменяемой строке, всегда будет случай, когда эти данные будут доступны в памяти в течение значительного периода времени после их удаления. Я не могу просто перезаписать это. Еще одно огромное преимущество, когда такие конфиденциальные данные хранятся в памяти в зашифрованном виде.