Коротка відповідь
чому я не можу просто сказати:
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
. Нативний об'єкт пам'яті, такий як:
- a BSTR
- HGLOBAL
- CoTaskMem некерована пам'ять
SecureString повертає втрачену здатність
У .NET, рядки не можна стерти, коли ви закінчите з ними:
- вони незмінні; ви не можете перезаписати їх вміст
- ви не можете
Dispose
з них
- їх прибирання - на розсуд сміттєзбірника
SecureString існує як спосіб забезпечити безпеку рядків і бути в змозі гарантувати їх очищення, коли потрібно.
Ви задали питання:
чому я не можу просто сказати:
SecureString password = new SecureString("password");
Тому що тепер у вас є password
пам’ять; без способу його витерти. Він застряг там, поки CLR не вирішить повторно використовувати цю пам'ять. Ви поставили нас прямо там, де ми почали; запущена програма з паролем, якої ми не можемо позбутися, і де дамп пам'яті (або Монітор процесів) може бачити пароль.
SecureString використовує API захисту даних для зберігання зашифрованого рядка в пам'яті; таким чином, рядок не буде існувати в swapfiles, краш-дампах або навіть у вікні локальних змінних, коли колега переглядає ваші потреби.
Як прочитати пароль?
Тоді виникає питання: як я взаємодію зі струною? Ви абсолютно не хочете такого способу, як:
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 є багато місць, де вони продовжують приймати звичайну String для цілей сумісності, а потім швидко перетворюють її, перекладаючи її в SecureString.
Як ввести текст у SecureString?
Це все ще залишає проблему:
Як я можу отримати пароль в SecureString в першу чергу?
Це виклик, але справа в тому, щоб ви задумалися про безпеку.
Іноді функціональність вам вже надана. Наприклад, керування WPF PasswordBox може повернути вам введений пароль безпосередньо як SecureString :
Отримує пароль, який наразі зберігається у 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? Ну , що вам потрібно зробити , це зупинка , має пароль в String , в першу чергу. Вам потрібно було мати щось інше. Навіть aChar[]
масив був би корисним.
Ось тоді ви можете додати кожен символ і витерти простим текстом, коли закінчите:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
Вам потрібен ваш пароль, який зберігається в деякій пам'яті, яку ви можете стерти. Завантажте його в SecureString звідти.
tl; dr: SecureString існує, щоб забезпечити еквівалент ZeroMemory .
Деякі люди не бачать сенсу стирання пароля користувача з пам'яті, коли пристрій заблоковано , або витирання натискань клавіш із пам'яті після їх автентифікації . Ці люди не використовують SecureString.
SecureString
нових розробок: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md