Является ли SecureString когда-либо практичным в приложении C #?

Не стесняйтесь исправить меня, если мои предположения здесь неправильны, но позвольте мне объяснить, почему я спрашиваю.

Взятый из MSDN, SecureString :

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

Я получаю это, имеет смысл хранить пароль или другую личную информацию в SecureString через System.String , потому что вы можете контролировать, как и когда он фактически хранится в памяти, потому что System.String :

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

Однако, в случае приложения GUI (например, клиент ssh), SecureString должен быть построен из System.String . Все текстовые элементы управления используют строку в качестве базового типа данных .

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

Теперь пришло время войти на сервер. Угадай, что? Для аутентификации вам необходимо передать строку . Итак, давайте преобразуем нашу SecureString в System.String …. и теперь у нас есть строка в куче, не имея возможности заставить ее пройти сборку мусора (или записать 0 в буфер).

Я хочу сказать, что независимо от того, что вы делаете, где-то вдоль линии, SecureString будет преобразован в System.String , то есть он будет, по крайней мере, существовать в куче в какой-то момент (без каких-либо гарантий сбора мусора).

Моя точка зрения заключается не в том, есть ли способы обхода отправки строки в ssh-соединение или обход управления содержимым строки (создание настраиваемого элемента управления). Для этого вопроса вы можете заменить «ssh connection» на «регистрационную форму», «регистрационную форму», «форму оплаты», «формулу« еда-вы-кормит-ваш-щенок, но не ваш ребенок »», и т.п.

  • Итак, в какой момент использование SecureString фактически становится практичным?
  • Стоит ли лишнее время разработки полностью искоренить использование объекта System.String ?
  • Является ли весь смысл SecureString просто сокращением времени, в течение которого System.String находится в куче (уменьшая риск перехода к файлу физического подкачки)?
  • Если у злоумышленника уже есть средство для проверки кучи, то он, скорее всего, либо (A) уже имеет средства для чтения нажатий клавиш, либо (B) уже физически имеет машину … Так что использование SecureString не позволит ему добраться до данные в любом случае?
  • Это просто «безопасность через безвестность»?

Извините, если я задаю слишком сложные вопросы, любопытство только что улучшило меня. Не стесняйтесь отвечать на все или на все мои вопросы (или скажите мне, что мои предположения совершенно неверны). 🙂

На самом деле очень практическое использование SecureString .

Вы знаете, сколько раз я видел такие сценарии? (ответ: много!):

  • Пароль случайно появляется в журнале.
  • В какой-то момент отображается пароль – как только GUI показывал запущенную командную строку приложения, а в командной строке – пароль. К сожалению .
  • Использование профилировщика профилей для профильного программного обеспечения с вашим коллегой. Коллега видит ваш пароль в памяти. Звучит нереально? Не за что.
  • Я когда-то использовал программное обеспечение RedGate которое могло бы захватить «значение» локальных переменных в случае исключений, удивительно полезно. Хотя, я могу представить, что он будет записывать «строковые пароли» случайно.
  • Дамп сбоя, который включает строковый пароль.

Вы знаете, как избежать всех этих проблем? SecureString . Обычно он делает глупые ошибки как таковые. Как это избежать? Убедившись, что пароль зашифрован в неуправляемой памяти, и реальное значение может быть доступно только тогда, когда вы на 90% уверены, что делаете.

В этом смысле SecureString работает довольно легко:

1) Все зашифровано

2) Пользовательские вызовы AppendChar

3) Расшифруйте все в UNMANAGED MEMORY и добавьте символ

4) Зашифруйте все снова в UNMANAGED MEMORY.

Что делать, если пользователь имеет доступ к вашему компьютеру? Может ли вирус получить доступ ко всем SecureStrings ? Да. Все, что вам нужно сделать, это подключить себя к RtlEncryptMemory когда память расшифровывается, вы получите местоположение незашифрованного адреса памяти и прочитаете его. Вуаля! Фактически, вы можете создать вирус, который будет постоянно проверять использование SecureString и регистрировать все действия с ним. Я не говорю, что это будет непростая задача, но это можно сделать. Как вы можете видеть, «мощь» SecureString полностью исчезла, когда в вашей системе появился пользователь / вирус.

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

Кроме того, как отмечали другие, WPF поддерживает PasswordBox, который использует SecureString внутренне через свое свойство SecurePassword .

Суть заключается в следующем: если у вас есть конфиденциальные данные (пароли, кредитные карты, ..), используйте SecureString . Это то, что следует за C # Framework. Например, class NetworkCredential хранит пароль как SecureString . Если вы посмотрите на это , вы можете увидеть более ~ 80 различных применений в .NET framework SecureString .

Есть много случаев, когда вам нужно преобразовать SecureString в строку, потому что некоторые API ожидают ее.

Обычная проблема заключается в следующем:

  1. API – GENERIC. Он не знает, что есть конфиденциальные данные.
  2. API знает, что он имеет дело с конфиденциальными данными и использует «строку» – это просто плохая конструкция.

Вы подняли хороший момент: что происходит, когда SecureString преобразуется в string ? Это может произойти только из-за первой точки. Например, API не знает, что это конфиденциальные данные. Я лично этого не видел. Получение строки из SecureString не так просто.

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

Есть один интересный случай, который я видел. А именно, функция WinApi LogonUser берет LPTSTR в качестве пароля, а это значит, что вам нужно позвонить SecureStringToGlobalAllocUnicode . Это в основном дает вам незашифрованный пароль, который живет в неуправляемой памяти. Вам нужно избавиться от этого, как только вы закончите:

 // Marshal the SecureString to unmanaged memory. IntPtr rawPassword = Marshal.SecureStringToGlobalAllocUnicode(password); try { //...snip... } finally { // Zero-out and free the unmanaged string reference. Marshal.ZeroFreeGlobalAllocUnicode(rawPassword); } 

Вы всегда можете расширить class SecureString с помощью метода расширения, такого как ToEncryptedString(__SERVER__PUBLIC_KEY) , который предоставляет string экземпляр SecureString который зашифрован с использованием открытого ключа сервера. Только сервер может его расшифровать. Проблема решена: коллекция мусора никогда не увидит «оригинальную» строку, так как вы никогда не выставляете ее в управляемой памяти. Это именно то, что делается в PSRemotingCryptoHelper ( EncryptSecureStringCore(SecureString secureString) ).

И как-то очень почти-связанное: Mono SecureString вообще не шифрует . Реализация была прокомментирована, потому что .. ждите ее … «Это почему-то вызывает обрыв теста nunit» , что приводит к моему последнему моменту:

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

Есть несколько вопросов в ваших предположениях.

Прежде всего class SecureString не имеет конструктора String. Чтобы создать один, вы выделяете объект, а затем добавляете символы.

В случае с графическим интерфейсом или консолью вы можете легко передать каждую нажатую клавишу в защищенную строку.

Класс разработан таким образом, что вы не можете, по ошибке, получить доступ к хранящемуся значению. Это означает, что вы не можете получить string в виде пароля непосредственно из нее.

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

В .NET Framework у вас есть несколько classов, которые могут использовать SecureString

  • Элемент управления паролями WPF хранит пароль как SecureString внутри.
  • Свойство Password. System.Diagnostics.ProcessInfo – это SecureString.
  • Конструктор для X509Certificate2 берет SecureString для пароля.

(Больше)

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

Все это, с примерами, хорошо описано в документации MSDN SecureString

SecureString полезен, если:

  • Вы создаете его по характеру (например, из ввода консоли) или получаете его из неуправляемого API

  • Вы используете его, передавая его неуправляемому API (SecureStringToBSTR).

Если вы когда-либо преобразовали его в управляемую строку, вы победили ее цель.

ОБНОВЛЕНИЕ в ответ на комментарий

… или BSTR, как вы упоминаете, что не кажется более безопасным

После его преобразования в BSTR неуправляемый компонент, который потребляет BSTR, может обнулить память. Неуправляемая память более безопасна в том смысле, что ее можно сбросить таким образом.

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

Основной пример использования, который я видел, – это клиентское приложение, которое требует от пользователя ввода высокочувствительного кода или пароля. Пользовательский ввод может использоваться символом по символу для создания SecureString, тогда это может быть передано неуправляемому API, который обнуляет BSTR, который он получает после его использования. Любой последующий дамп памяти не будет содержать чувствительную строку.

В серверном приложении трудно понять, где это было бы полезно.

ОБНОВЛЕНИЕ 2

Одним из примеров .NET API, который принимает SecureString, является этот конструктор для classа X509Certificate . Если вы поделитесь с ILSpy или похожим, вы увидите, что SecureString внутренне преобразован в неуправляемый буфер ( Marshal.SecureStringToGlobalAllocUnicode ), который затем обнуляется по завершении ( Marshal.ZeroFreeGlobalAllocUnicode ).

Как вы правильно определили, SecureString предлагает одно конкретное преимущество перед string : детерминированное стирание. С этим фактом возникают две проблемы:

  1. Как упомянули другие, и, как вы заметили сами, этого само по себе недостаточно. Вы должны убедиться, что каждый шаг процесса (включая поиск ввода, построение строки, использование, удаление, protractorовку и т. Д.) Происходит без нарушения цели использования SecureString . Это означает, что вы должны быть осторожны, чтобы никогда не создавать неизменяемую string управляемую GC, или любой другой буфер, в котором будет храниться конфиденциальная информация (или вам также придется отслеживать это ). На практике это не всегда легко достичь, потому что многие API-интерфейсы предлагают только способ работать со string , а не с SecureString . И даже если вам все будет в порядке …
  2. SecureString защищает от очень специфических видов атак (и для некоторых из них он даже не настолько надежный). Например, SecureString позволяет сократить временное окно, в котором злоумышленник может выгрузить память вашего процесса и успешно извлечь конфиденциальную информацию (опять же, как вы правильно указали), но надеясь, что окно слишком мало для злоумышленника сделать снимок вашей памяти не считается безопасностью вообще.

Итак, когда вы должны его использовать? Только когда вы работаете с чем-то, что может позволить вам работать с SecureString для всех ваших потребностей, и даже тогда вы все равно должны помнить, что это безопасно только в определенных обстоятельствах.

Я хотел бы остановиться на этом:

Если у злоумышленника уже есть средства для проверки кучи, то они, скорее всего, либо (A) уже имеют средства для чтения нажатий клавиш, либо (B) уже физически имеют машину … Так что использование SecureString не позволит им добраться до данные в любом случае?

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

HeartBleed утечка памяти

Возьмите Heartbleed, например. Специально сконструированные запросы могут привести к тому, что код будет подвергать атакующего случайные части памяти процесса. Злоумышленник может извлекать сертификаты SSL из памяти, но единственное, что ему нужно – это просто использовать неверный запрос.

В мире управляемого кода переполнение буфера становится проблемой гораздо реже. А в случае WinForms данные уже хранятся в небезопасном режиме, и вы ничего не можете с этим поделать. Это делает защиту с SecureString практически бесполезной.

Однако GUI можно запрограммировать на использование SecureString , и в таком случае сокращение windows доступности пароля в памяти может стоить того. Например, PasswordBox.SecurePassword из WPF имеет тип SecureString .

Ниже текст скопирован из анализатора статического кода HP Fortify

Аннотация: Метод PassString () в PassGenerator.cs хранит конфиденциальные данные ненадежным образом (то есть в строке), что позволяет извлекать данные путем проверки кучи.

Объяснение: Чувствительные данные (например, пароли, номера социального страхования, номера кредитных карт и т. Д.), Хранящиеся в памяти, могут быть пропущены, если они хранятся в управляемом объекте String. Строковые объекты не закреплены, поэтому сборщик мусора может переместить эти объекты по желанию и оставить несколько копий в памяти. Эти объекты по умолчанию не зашифровываются, поэтому каждый, кто может прочитать память процесса, сможет увидеть содержимое. Кроме того, если память процесса выгружается на диск, незашифрованное содержимое строки будет записано в файл подкачки. Наконец, поскольку объекты String неизменяемы, удаление значения строки из памяти может выполняться только сборщиком мусора CLR. Сборщик мусора не требуется запускать, если CLR не является низкой в ​​памяти, поэтому нет никакой гарантии, что когда garbage collection будет иметь место. В случае сбоя приложения дамп памяти приложения может отображать конфиденциальные данные.

Рекомендации. Вместо хранения конфиденциальных данных в таких объектах, как «Строки», храните их в объекте SecureString. Каждый объект хранит его содержимое в зашифрованном формате в памяти в любое время.

  • Действиям ребенка не разрешено выполнять действия перенаправления после установки сайта на HTTPS
  • В каких случаях HTTP_REFERER будет пустым
  • Защита CSRF: нужно ли нам генерировать токен для каждой формы?
  • Безопасность HTML5 localStorage
  • Будет ли веб-браузер кэшировать содержимое по https
  • Как я могу обеспечить уничтожение объекта String в Java?
  • IIS7, web.config, чтобы разрешить только статический обработчик файлов в каталоге / загрузках веб-сайта
  • Почему strtok () считается небезопасным?
  • Лучший способ ограничить доступ по IP-адресу?
  • Является ли HTTP-заголовком Referer отправленным при переходе на страницу http с https-страницы?
  • Безопасно ли использовать $ .support.cors = true; в jQuery?
  • Давайте будем гением компьютера.