Как SecureString "зашифрована" и все еще может использоваться?

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

Интересно, как такое шифрование возможно? Алгоритм будет фиксированным и, следовательно, либо общеизвестным, либо вычитаемым (скажем, один из семи широко используемых в промышленности алгоритмов), и где-то в программе должен быть ключ. Таким образом, злоумышленник может получить зашифрованную строку, получить ключ и расшифровать данные.

Чем может быть полезно такое шифрование?

4 ответа

Решение

Я цитирую статью о DPAPI, которая используется для получения ключа. Это должно ответить на большинство ваших вопросов о SecureString.

И да, у SecureString есть недостатки и она не полностью безопасна, существуют способы доступа к данным, например, введение " Соколиного Глаза в процесс" упоминается в MSDN как способ извлечения SecureString. Я лично не проверял это утверждение.

Управление ключами DAPI

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

Во введении я написал, что главный ключ генерируется из пароля пользователя для входа. Это не полная картина. На самом деле Windows использует пароль пользователя для создания мастер-ключа. Этот мастер-ключ защищен паролем пользователя и затем сохраняется вместе с профилем пользователя. Затем этот мастер-ключ используется для получения ряда других ключей, и именно эти другие ключи используются для защиты данных.

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

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

Когда пароль пользователя изменяется, тогда, конечно, генерируется новый главный ключ. Этот новый главный ключ затем используется для генерации новых индивидуальных ключей. Однако, поскольку все ранее созданные индивидуальные ключи были получены из старого мастер-ключа, Windows должна хранить все предыдущие мастер-ключи, что она и делает. Windows никогда не забывает главный ключ, и все защищенные данные помечаются GUID, который указывает, какой главный ключ использовался для защиты данных. Таким образом, с точки зрения адаптивности, DAPI способен справляться с изменениями паролей пользователей, обеспечивая при этом а) что защищенные данные не нуждаются в повторной защите, и б) ключи, которые ранее использовались для защиты данных как все еще доступных, и c) он делает все это автоматически для вас.

Если компьютер не является членом домена, DAPI может только незащищенные данные на том же компьютере, который использовался для его защиты.

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

Я посмотрел одну точку в коде для него, и он использует Windows advapi32 делать свою грязную работу. Таким образом, ключ не сохраняется в памяти приложения.

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static int SystemFunction040([In, Out] SafeBSTRHandle pDataIn, [In] uint cbDataIn, [In] uint dwFlag)

Который более известен как RtlEncryptMemory ,

Расшифровывается с RtlDecryptMemory (SystemFunction041).

Я уверен, что компилятор что-то делает с SecurityCriticalAttribute также.

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

Как уже отвечали другие, содержание SecureString зашифрованы с использованием DPAPI, поэтому ключи не хранятся в вашем приложении, они являются частью операционной системы. Я не на 100% уверен, но я бы предположил, что SecureString использует пользовательский ключ, так что даже если другой процесс получит доступ к блоку памяти, он должен будет работать с теми же учетными данными, чтобы просто расшифровать содержимое с помощью DPAPI. Даже если нет, машинный ключ (теоретически) не позволяет легко расшифровать строку при ее передаче в другую систему.

Более важно с SecureString как и когда вы используете его. Его следует использовать для хранения строковых данных, которые необходимо хранить в памяти в течение "продолжительных" периодов времени, но которые не часто необходимы в их расшифрованном виде. В какой-то момент вам придется расшифровать его в обычный старый System.Stringили System.Char[], Это когда он наиболее уязвим в памяти. Если вы делаете это слишком часто, то у вас есть несколько копий дешифрованной строки, плавающей в памяти и ожидающей сбора.

Как правило, если я читаю зашифрованные данные (например, учетные данные для входа), которые необходимо сохранить для нечастого использования (например, взаимодействие с PayPal или Amazon API), я сохраняю / кэширую эти учетные данные как SecureStringзатем расшифруйте его по мере необходимости достаточно долго, чтобы сделать вызовы веб-службы, и убедитесь, что срок службы любой дешифрованной копии составляет всего несколько строк кода.

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

Волшебством DPAPI:

Этот класс хранит свои данные с использованием защищенной модели памяти API защиты данных (DPAPI). Другими словами, данные всегда находятся в зашифрованном виде, пока они хранятся в SecureString. Ключ шифрования управляется локальной подсистемой безопасности (LSASS.EXE), а через DPAPI данные могут быть расшифрованы посредством межпроцессного взаимодействия.

Другие вопросы по тегам