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

179

Я пытаюсь обмануть цель 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 мне не хватает?

Ричард Морган
источник
3
11 лет спустя и MS больше не рекомендует SecureStringдля новой разработки: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md
Мэтт Томас,

Ответы:

4

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

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

Джо Хили
источник
1
Ссылка не работает, но, похоже, она продолжается: docs.microsoft.com/en-us/dotnet/api/… .. с некоторыми очень слабыми рекомендациями по пути вперед, за исключением "не использовать учетные данные" - github.com/dotnet/platform- compat / blob / master / docs / DE0001.md .. не смейте также использовать пароль для защиты закрытого ключа ваших сертификатов!
Фелицк
1
Спустя 11 лет этот ответ выглядит «новым» правильным. Ссылки, похоже, устарели, но руководство от MS гласит: SecureString не следует использовать
Ричард Морган,
109

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

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

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

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

Крис Уэнам
источник
2
Я наткнулся на это, глядя на свойство Password ProcessStartInfo; даже не обращая внимания на тип, я просто установил его на обычную строку, пока компилятор не залаял на меня.
Ричард Морган
Найти симметричный ключ шифрования будет нелегко, поскольку SecureString основан на DPAPI, который не хранит ключ в незашифрованном виде ...
AviD
1
Кроме того, это не столько проблема курицы и яйца, поскольку это не замена шифрования в хранилище, а обходной путь для неизменяемых управляемых строк .NET.
AviD
2
"у вас, вероятно, уже есть секретное значение в виде простой строки, так какой смысл?" Есть ли ответ на этот вопрос? В лучшем случае кажется, что это решение «лучше, чем ничего», если вы собираетесь хранить пароль в памяти в течение длительного периода времени.
xr280xr
2
Как насчет нескольких простых примеров использования кода? Думаю, я бы лучше понял, как и когда его использовать.
код
37

Изменить : не использовать 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.

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

Ричард Морган
источник
19

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

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

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.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.

Ян Бойд
источник
14

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

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

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

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

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

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

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

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

Джо
источник
12

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

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

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

JoshReedSchramm
источник
11

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

kemiller2002
источник
5

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

Джейсон З
источник
4

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

OregonGhost
источник
4

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

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

Марк Бесси
источник
2
Если они ограничивают конструкцию константной строкой, то строка foreach (char c в «password» .ToCharArray ()) победит это, нет? Это должно быть pass.AppendChar ('p'); pass.AppendChar ( 'а'); и т.д.?
Ричард Морган
Да, вы можете легко отбросить ту небольшую защиту, которую обеспечивает вам SecureString. Они пытаются сделать все возможное, чтобы полностью выстрелить себе в ногу. Очевидно, должен быть какой-то способ получить значение в SecureString и из него, иначе вы не сможете использовать его ни для чего.
Марк Бесси
1

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

Римский
источник