Optymalizacja klasy String

Wbudowany mechanizm optymalizacji może czasami przynieść zaskakujące wyniki. Rozważmy poniższy fragment kodu:

string var1 = "text";
string var2 = "text";

bool condition = object.ReferenceEquals(var1, var2);

Wydawałoby się, że var1 i var2 stanowią dwie osobne referencje. Po uruchomieniu kodu przekonamy się jednak, że zmienna condition będzie miała wartość true. Spowodowane jest to wykonaną optymalizacją, polegającą na tym, że .NET przechowuje zbiór użytych w programie napisów. Deklarując  zmienną przechowującą napis “text” pierwszy raz, .NET zapamiętuje napis w specjalnym buforze. Następnie gdy zostanie zadeklarowana ponownie ta sama wartość(“text”), nie nastąpi ponowna alokacja pamięci, a zwykłe przypisanie wskaźnika na wcześniej zapisaną wartość w buforze. Struktura adresowania wygląda więc mniej więcej tak:

image

Niestety nie zawsze może nastąpić optymalizacja. Poniższy kod nie zostanie zoptymalizowany i wartość condition będzie miała wartość false:

string var1 = "text";
string var2 = "tex";
var2 += "t";

bool condition = object.ReferenceEquals(var1, var2);

Przechowywanie poufnych informacji

W kodzie często używamy klasy Stirng do przechowywania poufnych informacji takich jak np. hasło. Niestety często nie zdajemy sobie sprawy jak niebezpieczne jest takie rozwiązanie. String jest specjalnie zoptymalizowaną klasą przeznaczoną do przechowywania łańcucha znaków, która jednak nie jest odporna na wszelkie ataki związane z podglądaniem pamięci operacyjnej. W skrócie pisząc, wszelkie informacje klasa String trzyma w postaci jawnej. Korzystając więc z odpowiedniego oprogramowania, intruz może bez problemu podejrzeć co aplikacja trzyma w klasie String. Ponadto ze względów na wspomnianą optymalizację String posiada następujące wady(w kwestii bezpieczeństwa):

  1. Brak możliwości wyczyszczenia bufora String. Nawet gdy przypiszemy wartość NULL, napis pozostaje wciąż zapisany w pamięci.
  2. Garbage Collector może robić kopie wartości String. Napisy nie są przechowywane ciągle pod tym samym adresem.
  3. Brak jakiejkolwiek metody szyfrowania danych.

W środowisku .NET rozwiązaniem na powyższe problemy jest klasa System.Security.SecureString. Moim zdaniem jest dosyć niewygodna w użyciu, jednak i tak wartość z niej korzystać.

Przykład zapisania do SecureString wartości podanej przez użytkownika(4 znaki):

SecureString secureString = new SecureString();

secureString.AppendChar(Console.ReadKey().KeyChar);
secureString.AppendChar(Console.ReadKey().KeyChar);
secureString.AppendChar(Console.ReadKey().KeyChar);
secureString.AppendChar(Console.ReadKey().KeyChar);

Następnie powinno się zablokować dostęp do wszelkich modyfikacji:

secureString.MakeReadOnly();

Wszelkie próby modyfikacji od tej chwili zakończą się wyrzuceniem wyjątku InvalidOperationException.