Как защитить строки без SecureString?

#c# #encryption #securestring

#c# #шифрование #securestring

Вопрос:

Вариант использования — защитить строки в программировании памяти на c #. Использование класса SecureString (https://learn.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netframework-4.7.2 ) не одобряется самой Microsoft.

Мне было интересно, может ли это быть действительной альтернативой:

  • преобразуйте строку в массив байтов и немедленно установите для строки значение null (и в конечном итоге вызовите сборщик мусора),
  • зашифруйте массив байтов с помощью класса ProtectedMemory.

Есть предложения?

Комментарии:

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

2. @PanagiotisKanavos «Даже если вы зашифруете строку, она должна начинаться как открытый текст, прежде чем ее можно будет зашифровать» — это неверно. SecureString.Append означает, что вы можете создать защищенную строку из пользовательского ввода (например, для каждого нажатия клавиши) без необходимости хранить открытый текст.

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

4. @PanagiotisKanavos Причина, по которой номер кредитной карты был открытым текстом до того, как он был зашифрован, заключается в том, что пользователь ввел его как открытый текст. Мы хотим быть уверены, что как можно быстрее удалим это из памяти. Если аргумент заключается в том, что мы никогда не должны беспокоиться: это нормально. Но Windows по-прежнему обнуляет память, прежде чем передать вам страницу размером 4 КБ (поэтому вы не можете видеть, что было на странице раньше), и она все еще есть SecureZeroMemory .

5. @IanBoyd аргумент заключается в том, что . Команда NET говорит, что SecureString небезопасен и обеспечивает очень ограниченную безопасность, создавая иллюзию безопасности. Исходный текст кредитной карты будет храниться в памяти до тех пор, пока он не будет собран. И как только вы используете Windows API или что-либо, что не понимает SecureString, вы получаете строку обратно.

Ответ №1:

Альтернативы SecureString классу нет. «Альтернативу», которую рекомендует Microsoft, можно найти здесь:

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

Итак, если вам действительно нужны учетные данные и другого способа нет: используйте .NET Framework SecureString . Для .NET Core на данный момент альтернативы нет.

Ответ №2:

Я бы не сказал, что это «не рекомендуется Microsoft» — это чрезмерное упрощение. Фактические причины приведены на этой странице ( https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md ) и аргумент, похоже, звучит так: «не стоит тратить усилия на его использование в .NET Core», а не на то, что он небезопасен в целом.

Я утверждаю, что SecureString это безопасно… но только для .NET Framework в Windows. Страница, на которую я ссылался, взята из проекта .NET Core, который является кроссплатформенным, поэтому имеет смысл запретить или запретить использование SecureString в .NET Core, но если ваш проект нацелен на .NET Framework (который является эксклюзивным для Windows) или нацелен на .NET Core для Windows — тогда все в порядке. Цитата приведена ниже (курсив мой):

Содержимое массива не зашифровано, за исключением .NET Framework.

Кстати, SecureString может использоваться безопасно, чтобы избежать открытого текста в памяти, если вы только читаете секреты непосредственно SecureString непосредственно, используя его Append метод. Это наиболее полезно при чтении паролей из консоли (псевдокод):

 Console.WriteLine( "Enter your password" );
SecureString password = new SecureString();
while( Char c = Console.ReadKey() != '[Enter'] ) {
    password.Append( c );
}
  

… однако, если вам нужен доступ к открытой текстовой версии строки впоследствии, тогда она менее безопасна (хотя, надеюсь, строка открытого текста будет собрана GC как объект поколения 0).

Что касается вашего предложения:

  • преобразуйте строку в массив байтов и немедленно установите для строки значение null (и в конечном итоге вызовите сборщик мусора)
  • зашифруйте массив байтов с помощью класса ProtectedMemory.

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

Комментарии:

1. Привет, Дай. Можете ли вы исправить это небольшое несоответствие, которое я вижу в вашем ответе. «Я утверждаю, что SecureString безопасен… но только для .NET Framework в Windows «. но также «или нацелен на .NET Core для Windows — тогда все в порядке». Итак, я думаю, мы все согласны с тем, что dotnetcore на linux…is «не так хорошо, как мы хотели бы надеяться». но dotnetCORE НА windows..is развилка на дороге.

2. Я думаю, что некоторые из проблем, связанных с этим, я думаю, что есть три, а не две перестановки. «2» «dotnet framework» против «dot net core». Но я думаю, что есть 3 перестановки. (1) .NET FW для Windows (2) .NET Core для (запущенной) Windows (3) .NET Core не для (не запущенной) Windows. И я думаю (но прошу вашего мнения и моего раннего запроса о несоответствии), что «(2) .NET Core для (работающий на) Windows» по-прежнему предоставляет значение с SecureString (ПРИ ПРАВИЛЬНОМ ИСПОЛЬЗОВАНИИ, КОНЕЧНО). Я говорил в течение 10 лет (до существования .net core), что m $ должен был назвать его «KindaSecureString» 🙂

3. @granadaCoder В моем ответе нет никаких расхождений или противоречий: при условии, что ваш SecureString код используется в Windows, он максимально безопасен по сравнению с отсутствием безопасности вообще в Linux и macOS.

4. Я согласен с комментарием, который вы только что опубликовали. Однако в вашем ответе говорится «но только для .NET Framework в Windows». (акцент на «только» и «framework в Windows»). Вот где я вижу несоответствие.

5. @granadaCoder Я определил его как «.NET Framework для Windows», потому что до выпуска .NET Core в 2016 году существовали (и существуют) другие платформы, также называемые «.NET Framework» для платформ, отличных от Windows, таких как .NET Micro Framework, .NET / XNA для Xbox, .NET Compact Framework, Xamarin / Mono (до выпуска .NET Core) и другие.

Ответ №3:

Итак, основной вопрос, который вы задаете: «поскольку Microsoft не рекомендует использовать SecureString, могу ли я использовать свой собственный?».

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

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

Ответ №4:

tl; dr: SecureString — хорошая и правильная идея. Причина, по которой Microsoft больше не рекомендует это, заключается в том, что .NET Core не может этого иметь, потому что Linux не поддерживает шифрование.

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

  • Используйте массив символов
  • Зашифруйте его

Длинный ответ

Есть две причины, по которым вам нужна SecureString.

Первый из них похож на атаку HeartBleed, и причина, по которой существует SecureZeroMemory, и причина, по которой Windows всегда обнуляет страницу памяти, прежде чем предоставить ее вам: чтобы избежать утечки информации.

Бонусное чтение

Службы терминалов хранят пароли пользователей в незашифрованном виде в оперативной памяти.

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

Итак, для решения проблемы есть два элемента:

  1. Вы можете переключиться на массив символов.

Когда это массив, это означает, что вы можете стереть содержимое. Это означает, что вы можете сделать моральный эквивалент ZeroMemory того, когда вы закончите с конфиденциальным номером кредитной карты, закрытым ключом bitcoin, паролем, мастер-ключом Intel Blu-Ray: вы можете стереть их

Это решает проблему полной неспособности стереть строку C #

  1. Шифрование с помощью CryptProtectData

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

Это два основных элемента, которые предоставляет SecureString.

И оба вы можете реплицировать. Причина .В NET Core их нет не потому, что SecureString — плохая идея, или бесполезная идея, или неполная идея, или «не выполняет то, что обещает». Вместо этого это потому, что в Linux нет эквивалента CryptProtectData . И поскольку .NET Core должен быть кроссплатформенным, они должны соответствовать наименьшему общему знаменателю. Поэтому они разводят руками и говорят, удалите это.

Но SecureString так же действителен как концепция, как:

  • ZeroMemory
  • SecureZeroMemory
  • Windows обнуляет страницу оперативной памяти, прежде чем передать ее вашему процессу
  • почему, когда вы устанавливаете filevaliddata, Windows обнуляет содержимое файла

И любой, кто говорит иначе, откровенно лжет.

Напоминание — вы хотите использовать SecureString

Вы хотите использовать SecureString

Означает ли это, что номер кредитной карты в какой-то момент был в виде открытого текста в памяти:

Нет, причина в том, что он имеет очень ограниченное использование, и в большинстве случаев строка либо остается в памяти, либо появляется снова. Исходная строка остается в памяти до тех пор, пока она не будет GCd. И поскольку SecureString не имеет аналога в Win32 API (или Linux), исходная строка появится снова, как только приложение попытается что-либо с ней сделать. Даже в SDK только NetworkCredentials правильно его использовали, а SecureString не является концепцией Windows, поэтому, как только вы перешли к Windows API, он был преобразован обратно.

  • Да, в какой-то момент номер кредитной карты был в виде открытого текста в памяти.
  • Да, пароль пользователя bitlocker в какой-то момент был в памяти.
  • Да, PIN-код пользователя iOS в какой-то момент был в памяти.
  • Да, закрытый ключ биткойна в какой-то момент был в памяти.
  • Да, данные в зашифрованном файле подкачки в какой-то момент были незашифрованы в ОЗУ.

Но мы должны понимать, что:

  • Это не значит, что вы не должны стирать его из памяти
  • это не значит, что вы не должны препятствовать его появлению в файле журнала
  • это не значит, что вы не должны препятствовать его появлению в файле подкачки
  • это не значит, что вы не должны предотвращать его появление в файле аварийного дампа
  • это не значит, что вы не должны препятствовать его появлению в окне просмотра
  • это не значит, что вы не должны препятствовать его появлению в моментальном снимке

Это хорошие меры углубленной защиты.

И любой, кто говорит иначе, просто неправ.

Комментарии:

1. Нет, причина в том, что он имеет очень ограниченное использование, и в большинстве случаев строка либо остается в памяти, либо появляется снова. Исходная строка остается в памяти до тех пор, пока она не будет GCd. И поскольку SecureString не имеет аналога в Win32 API (или Linux), исходная строка появится снова, как только приложение попытается что-либо с ней сделать. Даже в SDK только NetworkCredentials правильно его использовали и SecureString isn't a windows concept, so once you went down to a Windows API it was converted back.

2.Microsoft не портировала SecureString в Linux, потому что это не рекомендуется, а не наоборот. Shawn we simply don't believe it has any protection in the real world. At some point, to get used, it gets turned back into a string and all bets are off. We haven't recommended it in years. Твитнуть

3. На самом деле SecureString считается вредным по сравнению с другими устаревшими типами. Сравните это с SecureString, где существование типа наносит активный вред клиентам, которые его используют, и впоследствии не проходит аудит. (От проблемы GH до устаревания класса)

4. @PanagiotisKanavos Если действительно есть люди, которые считают SecureString , что это плохо, то мы можем создать новый класс, который: а) хранит строку в неуправляемой памяти, б) шифрует ее с CryptProtectMemory помощью . Мы можем вызвать этот класс ProtectedString . Таким образом, любой, кто жалуется на SecureString, может ошибаться и быть удовлетворен тем, что мы сделали то, что они просили. Бонус: «.NET не поддерживает шифрование во всех средах либо из-за отсутствия API, либо из-за проблем с управлением ключами». Это ошибка других сред, а не людей, пытающихся использовать шифрование.