Когда мне понадобится SecureString в .NET?

Я пытаюсь понять цель .NET SecureString. Из 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 мне не хватает?

Ответ 1

Я бы прекратил использовать SecureString. Похоже, парни PG отказываются от поддержки. Возможно, даже вытащить его в будущем - https://github.com/dotnet/apireviews/tree/master/2015-07-14-securestring.

Мы должны удалить шифрование из SecureString на всех платформах .NET Core. Мы должны устареть SecureString. Мы, вероятно, не должны открывать SecureString в .NET Core

Ответ 2

Некоторые части платформы, которые в настоящее время используют SecureString:

Основная цель - уменьшить поверхность атаки, а не устранить ее. SecureStrings "закреплены" в оперативной памяти, поэтому сборщик мусора не будет перемещать его или копировать. Это также гарантирует, что простой текст не будет записан в файл подкачки или в дампы ядра. Шифрование больше похоже на обфускацию и не остановит решительного хакера, который сможет найти симметричный ключ, используемый для его шифрования и дешифрования.

Как уже говорили другие, причина, по которой вы должны создавать символьный символ SecureString, заключается в первом очевидном недостатке, заключающемся в том, чтобы поступить иначе: вы, вероятно, уже имеете секретное значение в виде простой строки, так какой в этом смысл?

SecureString - это первый шаг в решении проблемы Chicken-and-Egg, поэтому, хотя большинство современных сценариев требуют преобразования их обратно в обычные строки, чтобы вообще их использовать, их существование в среде теперь означает лучшую поддержку для них в Будущее - по крайней мере, до такой степени, что ваша программа не должна быть слабым звеном.

Ответ 3

Изменить: не использовать SecureString

Текущее руководство теперь говорит, что класс не должен использоваться. Подробности можно найти по этой ссылке: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

Из статьи:

DE0001: SecureString не должен использоваться

мотивация

  • Цель SecureString - избежать хранения секретов в памяти процесса в виде простого текста.
  • Однако даже в Windows SecureString не существует в качестве концепции ОС.
    • Это просто делает окно короче простого текста; это не полностью предотвращает это, поскольку .NET все еще должен преобразовать строку в текстовое представление.
    • Преимущество заключается в том, что текстовое представление не является экземпляром System.String - время жизни собственного буфера короче.
  • Содержимое массива является незашифрованным, за исключением .NET Framework.
    • В .NET Framework содержимое внутреннего массива char зашифровано. .NET не поддерживает шифрование во всех средах, либо из-за отсутствия API, либо из-за проблем с управлением ключами.

Рекомендация

Не используйте SecureString для нового кода. При переносе кода на .NET Core учтите, что содержимое массива не зашифровано в памяти.

Общий подход к работе с учетными данными - избегать их и вместо этого полагаться на другие средства аутентификации, такие как сертификаты или проверка подлинности Windows.

Конец редактирования: Исходное резюме ниже

Много хороших ответов; Вот краткий обзор того, что обсуждалось.

Microsoft внедрила класс SecureString, стремясь обеспечить более надежную защиту конфиденциальной информацией (например, кредитными картами, паролями и т.д.). Это автоматически обеспечивает:

  • шифрование (в случае дампов памяти или кэширования страниц)
  • закрепление в памяти
  • возможность пометить только для чтения (для предотвращения дальнейших изменений)
  • безопасная конструкция, НЕ позволяющая передавать константную строку в

В настоящее время SecureString ограничен в использовании, но в будущем ожидают лучшего внедрения.

Основываясь на этой информации, конструктор SecureString не должен просто брать строку и разрезать ее до массива char, поскольку указанная строка не соответствует цели SecureString.

Дополнительная информация:

  • Сообщение из блога .NET Security, в котором говорится о том же, что и здесь.
  • И еще один пересмотр его и упоминание инструмента, который МОЖЕТ сбросить содержимое SecureString.

Изменение: мне было трудно выбрать лучший ответ, так как есть много информации; Жаль, что нет вариантов ответа с помощью.

Ответ 4

Краткий ответ

почему я не могу просто сказать:

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 защиты данных для хранения строки, зашифрованной в памяти; таким образом, строка не будет существовать в swapfiles, аварийных дампах или даже в окне локальных переменных с коллегой, просматривающим ваши сообщения.

Как я могу прочитать пароль?

Тогда возникает вопрос: как мне взаимодействовать со строкой? Вы абсолютно не хотите метод вроде:

String connectionString = secureConnectionString.ToString()

потому что теперь вы вернулись туда, где вы начали, - пароль, от которого вы не можете избавиться. Вы хотите, чтобы разработчики force правильно обрабатывали чувствительную строку - чтобы их можно было стереть из памяти.

Вот почему .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 есть много мест, где они продолжают принимать простой String для целей совместимости, а затем быстро поворачивают его в 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.

Ответ 5

Существует очень мало сценариев, в которых вы можете разумно использовать SecureString в текущей версии Framework. Это действительно полезно только для взаимодействия с неуправляемыми API-интерфейсами - вы можете его маршалировать с помощью Marshal.SecureStringToGlobalAllocUnicode.

Как только вы конвертируете его в/из System.String, вы победили его цель.

Пример MSDN генерирует символ SecureString за один раз с входа в консоль и передает защищенную строку неуправляемому API. Это довольно запутанно и нереально.

Вы можете ожидать, что будущие версии .NET получат дополнительную поддержку SecureString, что сделает ее более полезной, например:

  • SecureString Console.ReadLineSecure() или аналогично считыванию ввода в консоль SecureString без всякого свернутого кода в образце.

  • Замена WinForms TextBox, которая сохраняет свойство TextBox.Text в качестве защищенной строки, чтобы пароли могли быть введены безопасно.

  • Расширения для связанных с безопасностью API, позволяющих передавать пароли как SecureString.

Без вышеуказанного SecureString будет иметь ограниченное значение.

Ответ 6

Я полагаю, что причина, по которой вам приходится делать добавление символов вместо одного плоского экземпляра, заключается в том, что в фоновом режиме передача "пароля" конструктору SecureString помещает эту строку "пароль" в память, разбирая цель безопасной строки.

Добавляя, вы только помещаете персонаж за раз в память, который не должен быть смежным друг с другом физически, что значительно усложняет восстановление исходной строки. Я мог ошибаться, но это объяснялось мне.

Целью класса является предотвращение защиты защищенных данных с помощью дампа памяти или аналогичного инструмента.

Ответ 7

MS обнаружила, что в некоторых случаях, когда сервер (рабочий стол, любой) разбивался, были ситуации, когда среда выполнения выполняла дамп памяти, отображая содержимое содержимого в памяти. Secure String шифрует его в памяти, чтобы злоумышленник не смог получить содержимое строки.

Ответ 8

Одним из больших преимуществ SecureString является то, что предполагается, что вы не сможете хранить данные на диске из-за кэширования страниц. Если у вас есть пароль в памяти, а затем загрузите большую программу или набор данных, ваш пароль может быть записан в файл подкачки, так как ваша программа выгружается из памяти. С SecureString, по крайней мере, данные не будут бесконечно сидеть на вашем диске в виде чистого текста.

Ответ 9

Я предполагаю, потому что строка предназначена для обеспечения безопасности, т.е. хакер не должен ее читать. Если вы инициализируете его строкой, хакер может прочитать исходную строку.

Ответ 10

Ну, как сказано в описании, значение хранится в зашифрованном виде, так что дамп памяти вашего процесса не будет показывать строковое значение (без какой-либо довольно серьезной работы).

Причина, по которой вы не можете просто построить SecureString из постоянной строки, состоит в том, что тогда у вас будет незашифрованная версия строки в памяти. Ограничение на создание строки на куски снижает риск одновременного хранения всей строки.

Ответ 11

Другой вариант использования - это когда вы работаете с платежными приложениями (POS), и вы просто не можете использовать неизменяемые структуры данных для хранения конфиденциальных данных, потому что вы являетесь осторожным разработчиком. Например: если я сохраню данные конфиденциальной карты или метаданные авторизации в неизменяемую строку, всегда будет иметь место, когда эти данные будут доступны в памяти в течение значительного времени после ее отбрасывания. Я не могу просто перезаписать его. Еще одно огромное преимущество, когда такие конфиденциальные данные хранятся в зашифрованном виде.