Хорошо известно, что С# string
довольно небезопасен, он не закреплен в ОЗУ, сборщик мусора может перемещать его, копировать, оставлять несколько следов в ОЗУ, а ОЗУ можно поменять местами и быть доступным как файл, который нужно прочитать, не говоря уже о нескольких других известных фактах.
Чтобы смягчить эту проблему, Microsoft придумала SecureString
. Дело в том, что какой способ его использовать?
Я спрашиваю об этом, потому что рано или поздно вам придется извлечь внутреннюю строку. И да, SecureString
позволяет нам писать один char
за один раз и дает нам некоторые другие незначительные обычаи, скрывая его в секрете, но иногда нам нужна целая строка сразу или что-то в этом роде. До сих пор все реализации, которые я вижу, используют в конце концов регулярный string
, с извлеченным и расшифрованным контентом из SecureString
, который, как я думаю (или надеюсь), можно избежать в некоторых случаях, даже если он может включать в себя сложный код .NET string
.
В моем случае я использую библиотеку для хэширования паролей под названием BCrypt. Он использует обычный string
для вычисления хэшей, который, как я думаю, далек от идеала. Я не нашел никаких библиотек, которые бы принимали SecureString
напрямую, поэтому у меня нет большого выбора, но используйте его.
В настоящее время я запускаю код, например:
SecureString password; // This usually comes from a PasswordBox Property
IntPtr passwordBSTR = Marshal.SecureStringToBSTR(password);
string insecurePassword = Marshal.PtrToStringBSTR(passwordBSTR);
Marshal.ZeroFreeBSTR(passwordBSTR);
passwordBSTR = default(IntPtr);
password.Clear();
// Use the insecurePassword....usually as:
bool result = BCrypt.Net.BCrypt.Verify(insecurePassword, user.Password); // This hashes the provided password and compares it with another BCrypt hash.
insecureString = string.Empty; // hoping for the GC to act now, fingers crossed.
// use result and etc...
Таким образом, пароль raw string
будет скопирован несколько раз для метода BCrypt
и, возможно, еще больше времени внутри него.
Это вызывает у меня некоторые вопросы:
1 - Есть строки BSTR
, такие как строки C
в смысле управления памятью?. Они прикреплены в ОЗУ, и я могу ими манипулировать и стирать их, как я считаю нужным, так что это будет более безопасно использовать их на С# или переписывать с помощью кода на С++, чтобы я мог устранить многие мои трюки .NET string
(например, проверить, является ли строка пароля нулевой или пробельной или имеет правильную длину или имеет желаемый пароль прочность)?
2 - Если вопрос № 1 ответ "да", тогда следует потратить некоторое время на то, чтобы передать строку BSTR на код взаимодействия С++ для вычисления хеша и проверить?
3 - Является ли мой более поздний код правильным или я что-то упустил при манипуляции SecureString
?
Только для того, чтобы быть ясным: Мое основное намерение с этим вопросом - оценить, должен ли я передать весь код обработки пароля на С++ из моего С# (возможно, используя С++/CLI или interop) для достижения более безопасный код, чем предлагает С#. Этот сценарий действительно касается меня для хэширования строки пароля, поскольку будут созданы и скопированы несколько строк с секретной информацией пользователя, разбросанной по всей ОЗУ.
В качестве примера: я мог бы сделать такой метод, как:
public string SecureHash(SecureString password)
И этот метод будет извлекать значение SecureString
и передавать его как строку BSTR
(или другую опцию извлечения, я, честно говоря, не знаю преимуществ между ними), методу хэширования С++, поэтому мы можем контролировать все места, где наш пароль находится в памяти.
Я прошу, чтобы у меня не было огромной работы по рефакторингу моего кода, и в конце концов выяснилось, что все это было ни к чему, потому что я использовал SecureStrings
неправильно все время, и есть уже решения для этого, или ложные как это бывает иногда, поскольку я не знаю, как строки BSTR
реализованы в памяти или другие SecureStrings
параметры извлечения (Unicode, ANSI и т.д.).