Krótka odpowiedź
dlaczego nie mogę po prostu powiedzieć:
SecureString password = new SecureString("password");
Ponieważ teraz masz password
w pamięci; bez możliwości jego wyczyszczenia - właśnie o to chodzi w SecureString .
Długa odpowiedź
Powodem SecureString istnieje dlatego, że nie można używać ZeroMemory wytrzeć poufnych danych, gdy jesteś z nim zrobić. Istnieje on do rozwiązania problemu, który istnieje , ponieważ CLR.
W zwykłej natywnej aplikacji dzwoniłbyś SecureZeroMemory
:
Wypełnia blok pamięci zerami.
Uwaga : SecureZeroMemory jest identyczny ZeroMemory
, z wyjątkiem tego , że kompilator go nie zoptymalizuje.
Problem polega na tym, że nie można dzwonić ZeroMemory
ani korzystać z SecureZeroMemory
platformy .NET. A w .NET ciągi są niezmienne; nie możesz nawet zastąpić zawartości ciągu, jak w innych językach:
//Wipe out the password
for (int i=0; i<password.Length; i++)
password[i] = \0;
Więc co możesz zrobić? W jaki sposób zapewniamy w .NET możliwość czyszczenia hasła lub numeru karty kredytowej z pamięci, gdy skończymy z tym?
Jedynym sposobem, w jaki można to zrobić, byłoby umieszczenie łańcucha w jakimś rodzimym bloku pamięci, do którego można następnie wywołać ZeroMemory
. Natywny obiekt pamięci, taki jak:
- a BSTR
- HGLOBAL
- CoTaskMem niezarządzana pamięć
SecureString przywraca utraconą zdolność
W .NET ciągi nie mogą być czyszczone, gdy już z nimi skończysz:
- są niezmienne; nie można zastąpić ich zawartości
- nie możesz
Dispose
z nich
- ich sprzątanie jest na łasce śmieciarza
SecureString istnieje jako sposób na ominięcie bezpieczeństwa ciągów i być w stanie zagwarantować ich czyszczenie w razie potrzeby.
Zadałeś pytanie:
dlaczego nie mogę po prostu powiedzieć:
SecureString password = new SecureString("password");
Ponieważ teraz masz password
w pamięci; bez możliwości jego wyczyszczenia. Utknął tam, dopóki CLR nie zdecyduje się ponownie użyć tej pamięci. Umieściłeś nas tam, gdzie zaczęliśmy; działająca aplikacja z hasłem, którego nie możemy się pozbyć i gdzie zrzut pamięci (lub Process Monitor) może zobaczyć hasło.
SecureString używa Data Protection API do przechowywania łańcucha zaszyfrowanego w pamięci; w ten sposób łańcuch nie będzie istniał w plikach wymiany, zrzutach awaryjnych, a nawet w oknie zmiennych lokalnych, gdy kolega powinien się nad nim zastanowić.
Jak odczytać hasło?
Następnie pojawia się pytanie: jak wchodzić w interakcje z ciągiem? Absolutnie nie chcesz takiej metody jak:
String connectionString = secureConnectionString.ToString()
ponieważ teraz wracasz do miejsca, w którym zacząłeś - hasła, którego nie możesz się pozbyć. Chcesz zmusić programistów do prawidłowego obchodzenia się z wrażliwym łańcuchem - aby mógł zostać usunięty z pamięci.
Właśnie dlatego .NET zapewnia trzy przydatne funkcje pomocnicze, które pozwalają wprowadzić SecureString do niezarządzanej pamięci:
Przekształcasz ciąg w niezarządzany obiekt blob pamięci, obsłużysz go, a następnie wyczyścisz ponownie.
Niektóre interfejsy API akceptują SecureStrings . Na przykład w ADO.net 4.5 SqlConnection.Credential przyjmuje zestaw SqlCredential :
SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();
Możesz także zmienić hasło w ciągu połączenia:
SqlConnection.ChangePassword(connectionString, cred, newPassword);
Wewnątrz .NET znajduje się wiele miejsc, w których nadal akceptują zwykły ciąg znaków dla celów kompatybilności, a następnie szybko odwracają go i umieszczają w SecureString.
Jak wstawić tekst do SecureString?
Pozostaje problem:
Jak w ogóle uzyskać hasło do SecureString?
To wyzwanie, ale chodzi o to, abyś pomyślał o bezpieczeństwie.
Czasami ta funkcja jest już dla Ciebie dostępna. Na przykład kontrolka WPF PasswordBox może zwrócić ci wprowadzone hasło bezpośrednio jako SecureString :
Pobiera hasło aktualnie przechowywane przez PasswordBox jako SecureString .
Jest to pomocne, ponieważ wszędzie tam, gdzie przekazywano nieprzetworzony ciąg znaków, teraz system typów narzeka, że SecureString jest niezgodny z ciągiem znaków. Chcesz przejść tak długo, jak to możliwe, zanim będziesz musiał przekonwertować SecureString z powrotem na zwykły ciąg.
Konwersja SecureString jest dość łatwa:
- SecureStringToBSTR
- PtrToStringBSTR
jak w:
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;
}
Oni naprawdę nie chcą, żebyś to robił.
Ale jak uzyskać ciąg do SecureString? Cóż, musisz przede wszystkim przestać mieć hasło w ciągu znaków . Musiałeś mieć to w czymś innym. Nawet aChar[]
tablica byłaby pomocna.
Właśnie wtedy możesz dołączyć każdą postać i wyczyścić tekst jawny, gdy skończysz:
for (int i=0; i < PasswordArray.Length; i++)
{
password.AppendChar(PasswordArray[i]);
PasswordArray[i] = (Char)0;
}
Musisz hasła zapisanego w jakiejś pamięci, które można wymazać. Załaduj go do SecureString stamtąd.
tl; dr: SecureString istnieje w celu zapewnienia odpowiednika ZeroMemory .
Niektóre osoby nie widzą sensu wymazywania hasła użytkownika z pamięci, gdy urządzenie jest zablokowane , ani wymazywania wymazania klawiszy z pamięci po ich uwierzytelnieniu . Ci ludzie nie używają SecureString.
SecureString
nowego oprogramowania: github.com/dotnet/platform-compat/blob/master/docs/DE0001.md