Когда мне понадобится SecureString в .NET? - PullRequest
178 голосов
/ 26 сентября 2008

Я пытаюсь поймать цель 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 ]

107 голосов
/ 26 сентября 2008

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

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

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

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

36 голосов
/ 27 сентября 2008

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

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

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

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

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

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

  • A post из .NET Security блог говорит о том же здесь
  • и еще один пересмотреть его и упомянуть инструмент что МОЖЕТ сбросить содержимое SecureString.

Редактировать: мне было трудно выбрать лучший ответ, так как во многих есть хорошая информация; Жаль, что нет вариантов ответа.

18 голосов
/ 17 декабря 2016

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

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

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.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? Что вам нужно сделать, так это прекратить вводить пароль в String . Вы должны были иметь это в что-то еще. Был бы полезен даже массив Char[].

Вот когда вы можете добавить каждый символ и , чтобы стереть открытый текст, когда вы закончите:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

Ваш пароль должен храниться в некоторой памяти, которую вы можете стереть. Загрузите его в SecureString оттуда.


tl; dr: SecureString существует для предоставления эквивалента ZeroMemory .

Некоторые люди не видят смысла в стирании пароля пользователя из памяти, когда устройство заблокировано , или стирании нажатий клавиш из памяти после аутентификации . Эти люди не используют SecureString.

14 голосов
/ 26 сентября 2008

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

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

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

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

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

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

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

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

12 голосов
/ 26 сентября 2008

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

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

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

11 голосов
/ 26 сентября 2008

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

5 голосов
/ 26 сентября 2008

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

4 голосов
/ 26 сентября 2008

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

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

4 голосов
/ 26 сентября 2008

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

3 голосов
/ 21 июля 2016

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...