Możesz mnie poprawić, jeśli moje założenia są tutaj błędne, ale pozwól mi wyjaśnić, dlaczego pytam.
Zaczerpnięte z MSDN SecureString
:
Reprezentuje tekst, który powinien być poufny. Tekst jest szyfrowany w celu zachowania prywatności podczas używania i usuwany z pamięci komputera, gdy nie jest już potrzebny.
Rozumiem, to ma sens przechowywać hasło lub inne prywatne informacje w ciągu SecureString
ponad System.String
, ponieważ możesz kontrolować, w jaki sposób i kiedy są one faktycznie przechowywane w pamięci, ponieważ System.String
:
jest zarówno niezmienny, jak i gdy nie jest już potrzebny, nie może być programowo zaplanowany do odśmiecania; oznacza to, że wystąpienie jest tylko do odczytu po utworzeniu i nie można przewidzieć, kiedy wystąpienie zostanie usunięte z pamięci komputera. W konsekwencji, jeśli obiekt String zawiera poufne informacje, takie jak hasło, numer karty kredytowej lub dane osobowe, istnieje ryzyko, że informacje mogą zostać ujawnione po ich użyciu, ponieważ aplikacja nie może usunąć danych z pamięci komputera.
Jednak w przypadku aplikacji GUI (na przykład klienta ssh) SecureString
należy ją zbudować z System.String
. Wszystkie kontrolki tekstu używają ciągu jako podstawowego typu danych .
Oznacza to, że za każdym razem, gdy użytkownik naciska klawisz, stary ciąg, który tam był, jest odrzucany, a nowy ciąg jest budowany, aby reprezentować wartość w polu tekstowym, nawet jeśli używa się maski hasła. Nie możemy kontrolować, kiedy lub czy którakolwiek z tych wartości zostanie usunięta z pamięci .
Czas zalogować się na serwerze. Zgadnij co? W celu uwierzytelnienia musisz przekazać ciąg znaków przez połączenie . Więc przekonwertujmy nasz SecureString
na System.String
... i teraz mamy ciąg na stosie, bez możliwości zmuszenia go do przechodzenia do wyrzucania elementów bezużytecznych (lub zapisania zer do bufora).
Chodzi mi o to : nie ważne co robisz, gdzieś wzdłuż linii, która SecureString
ma zamiar zostać przekształcony w System.String
, co oznacza, że będzie istnieć przynajmniej na stercie w pewnym momencie (bez gwarancji garbage collection).
Nie mam na myśli: czy istnieją sposoby na obejście wysyłania łańcucha do połączenia ssh, czy obchodzenie przechowywania kontroli w łańcuchu (wykonaj niestandardową kontrolę). W przypadku tego pytania możesz zastąpić „połączenie ssh” „formularzem logowania”, „formularzem rejestracyjnym”, „formularzem płatności”, „formularzem karmy-którą-nakarmisz-swoim-szczeniakiem-ale-nie-twoimi dziećmi”, itp.
- Więc w którym momencie używanie
SecureString
faktycznie staje się praktyczne? - Czy kiedykolwiek warto poświęcić dodatkowy czas na opracowanie, aby całkowicie wyeliminować użycie
System.String
obiektu? - Czy chodzi
SecureString
po prostu o to, aby po prostu ograniczyć czas, w którym aSystem.String
jest na stercie (zmniejszając ryzyko przejścia do fizycznego pliku wymiany)? - Jeśli atakujący ma już środki na kontrolę stosu, najprawdopodobniej albo (A) już ma środki do odczytu naciśnięć klawiszy, albo (B) już fizycznie ma maszynę ... Więc użyłby
SecureString
przeszkody, aby nie dostał się do dane w każdym razie? - Czy to tylko „bezpieczeństwo przez zaciemnienie”?
Przepraszam, jeśli kładę pytania zbyt gęste, ciekawość właśnie mnie ogarnęła. Odpowiedz na dowolne lub wszystkie moje pytania (lub powiedz, że moje założenia są całkowicie błędne). :)
SecureString
tak naprawdę nie jest to bezpieczny ciąg. Jest to tylko sposób na ograniczenie czasu, w którym ktoś może sprawdzić twoją pamięć i pomyślnie uzyskać poufne dane. To nie jest kuloodporne i nie było przeznaczone. Ale punkty, które zbierasz, są bardzo ważne. Powiązane: stackoverflow.com/questions/14449579/…Odpowiedzi:
Są bardzo praktyczne zastosowania
SecureString
.Czy wiesz, ile razy widziałem takie scenariusze? (odpowiedź brzmi: wielu!):
RedGate
oprogramowania, które mogło uchwycić „wartość” zmiennych lokalnych w przypadku wyjątków, niezwykle przydatne. Chociaż mogę sobie wyobrazić, że przypadkowo zarejestruje „ciąg hasła”.Czy wiesz, jak uniknąć tych wszystkich problemów?
SecureString
. Generalnie upewnia się, że nie popełniasz głupich błędów jako takich. Jak tego uniknąć? Upewniając się, że hasło jest szyfrowane w niezarządzanej pamięci, a do rzeczywistej wartości można uzyskać dostęp tylko wtedy, gdy masz 90% pewności, co robisz.W tym sensie
SecureString
działa dość łatwo:1) Wszystko jest szyfrowane
2) Połączenia użytkowników
AppendChar
3) Odszyfruj wszystko w NIEZARZĄDZANEJ PAMIĘCI i dodaj postać
4) Zaszyfruj wszystko ponownie w NIEZARZĄDZANEJ PAMIĘCI.
Co jeśli użytkownik ma dostęp do twojego komputera? Czy wirus byłby w stanie uzyskać dostęp do wszystkich
SecureStrings
? Tak. Wystarczy, że przyłączysz się doRtlEncryptMemory
odszyfrowywania pamięci, otrzymasz lokalizację niezaszyfrowanego adresu pamięci i przeczytasz go. Voila! W rzeczywistości możesz stworzyć wirusa, który będzie stale skanować w poszukiwaniu użyciaSecureString
i rejestrować za jego pomocą wszystkie działania. Nie twierdzę, że będzie to łatwe zadanie, ale da się to zrobić. Jak widać, „moc”SecureString
całkowicie zniknęła, gdy w twoim systemie pojawił się użytkownik / wirus.Masz kilka punktów w swoim poście. Jasne, jeśli użyjesz niektórych elementów sterujących interfejsu użytkownika, które przechowują „hasło ciągu” wewnętrznie, użycie faktycznego
SecureString
nie jest tak przydatne. Mimo to może chronić przed jakąś głupotą, którą wymieniłem powyżej.Ponadto, jak zauważyli inni, WPF obsługuje PasswordBox, który używa
SecureString
wewnętrznie poprzez swoją właściwość SecurePassword .Najważniejsze jest: jeśli masz wrażliwe dane (hasła, karty kredytowe, ...), użyj
SecureString
. Oto, co przestrzega Framework C #. Na przykładNetworkCredential
klasa przechowuje hasło jakoSecureString
. Jeśli spojrzysz na to , możesz zobaczyć ponad ~ 80 różnych zastosowań w .NET FrameworkSecureString
.Istnieje wiele przypadków, gdy trzeba przekonwertować
SecureString
na ciąg, ponieważ niektóre interfejsy API tego oczekują.Typowy problem to:
Podniosłeś rację: co się stanie, kiedy
SecureString
zostanie przekonwertowanystring
? Może się to zdarzyć tylko z powodu pierwszego punktu. Np. Interfejs API nie wie, że to wrażliwe dane. Nie widziałem tego osobiście. Pobieranie ciągu z SecureString nie jest takie proste.To nie jest proste z prostego powodu ; nigdy nie zamierzano pozwolić użytkownikowi przekonwertować SecureString na ciąg znaków, jak powiedziałeś: GC uruchomi się. Jeśli zobaczysz, że to robisz, musisz cofnąć się i zadać sobie pytanie: dlaczego to robię, czy naprawdę potrzebuję To dlaczego?
Widziałem jeden interesujący przypadek. Mianowicie, funkcja WinApi LogonUser przyjmuje LPTSTR jako hasło, co oznacza, że musisz zadzwonić
SecureStringToGlobalAllocUnicode
. Zasadniczo daje to niezaszyfrowane hasło, które znajduje się w niezarządzanej pamięci. Musisz się tego pozbyć, gdy tylko skończysz:Zawsze możesz rozszerzyć
SecureString
klasę za pomocą metody rozszerzenia, takiej jakToEncryptedString(__SERVER__PUBLIC_KEY)
, która dajestring
instancję,SecureString
która jest zaszyfrowana przy użyciu klucza publicznego serwera. Tylko serwer może go odszyfrować. Problem rozwiązany: Garbage Collection nigdy nie zobaczy „oryginalnego” ciągu, ponieważ nigdy nie ujawnia się go w pamięci zarządzanej. Dokładnie tak się dzieje wPSRemotingCryptoHelper
(EncryptSecureStringCore(SecureString secureString)
).I jako coś bardzo prawie związanego: Mono SecureString w ogóle nie szyfruje . Implementacja została skomentowana, ponieważ… czekaj na nią… „To w jakiś sposób powoduje zerwanie testu nunit” , co prowadzi do mojego ostatniego punktu:
SecureString
nie jest obsługiwany wszędzie. Jeśli platforma / architektura nie obsługujeSecureString
, otrzymasz wyjątek. W dokumentacji znajduje się lista platform obsługiwanych.źródło
SecureString
byłoby o wiele bardziej praktyczne, gdyby istniał mechanizm dostępu do ich poszczególnych postaci. Wydajność takiego mechanizmu można zwiększyć, mając typ strukturySecureStringTempBuffer
bez żadnych publicznych członków, który można by przekazaćref
do metodySecureString
„czytaj jeden znak” [jeśli szyfrowanie / deszyfrowanie jest przetwarzane w 8-znakowych fragmentach, taka struktura może przechowywać dane dla 8 znaków i lokalizację pierwszego z tych znaków wSecureString
].SecureString
go używasz , musi być używany przez cały stos.EngineSettings
metoda rozszerzenia?SecureString
nie należy jej używać do nowego programowania ?Jest kilka problemów w twoich założeniach.
Po pierwsze klasa SecureString nie ma konstruktora String. Aby go utworzyć, należy przydzielić obiekt, a następnie dołączyć znaki.
W przypadku GUI lub konsoli możesz bardzo łatwo przekazać każdy naciśnięty klawisz do bezpiecznego ciągu.
Klasa została zaprojektowana w taki sposób, że przez pomyłkę nie można uzyskać dostępu do przechowywanej wartości. Oznacza to, że nie można uzyskać
string
bezpośrednio z niego hasła.Aby więc na przykład użyć go do uwierzytelnienia w sieci, będziesz musiał użyć odpowiednich, bezpiecznych klas.
W środowisku .NET masz kilka klas, które mogą korzystać z SecureString
(więcej)
Podsumowując, klasa SecureString może być przydatna, ale wymaga większej uwagi ze strony programisty.
Wszystko to, wraz z przykładami, jest dobrze opisane w dokumentacji MSDN dotyczącej SecureString
źródło
SecureString
sieć bez szeregowania jej w siećstring
? Myślę, że chodzi o to, że pytanie polega na tym, że przychodzi czas, kiedy naprawdę chcesz bezpiecznie wykorzystać wartość przechowywaną w aSecureString
, co oznacza, że musisz ją stamtąd wyciągnąć i nie jest to już bezpieczne. W całej bibliotece klas frameworka praktycznie nie ma metod, które akceptująSecureString
dane wejściowe bezpośrednio (o ile widzę), więc jaki jest sens utrzymywania wartości w środkuSecureString
?char[]
i wysyłając każdychar
przez gniazdo - nie tworząc w ten sposób żadnych obiektów do zbierania śmieci.SecureString jest przydatny, jeśli:
Budujesz go znak po znaku (np. Z danych wejściowych konsoli) lub uzyskujesz go z niezarządzanego interfejsu API
Używasz go, przekazując go do niezarządzanego interfejsu API (SecureStringToBSTR).
Jeśli kiedykolwiek przekonwertujesz go na zarządzany ciąg, pokonałeś jego cel.
AKTUALIZACJA w odpowiedzi na komentarz
Po przekształceniu w BSTR niezarządzany składnik, który zużywa BSTR, może wyzerować pamięć. Niezarządzana pamięć jest bezpieczniejsza w tym sensie, że można ją zresetować w ten sposób.
Istnieje jednak bardzo niewiele interfejsów API w .NET Framework, które obsługują SecureString, więc masz rację, mówiąc, że ma dziś bardzo ograniczoną wartość.
Główny przypadek użycia, jaki widziałbym, to aplikacja kliencka, która wymaga od użytkownika wprowadzenia bardzo wrażliwego kodu lub hasła. Dane wejściowe użytkownika mogą być używane znak po znaku do zbudowania SecureString, a następnie mogą być przekazywane do niezarządzanego interfejsu API, który zeruje BSTR otrzymany po jego użyciu. Kolejny zrzut pamięci nie będzie zawierał wrażliwego ciągu.
W aplikacji serwerowej trudno jest zobaczyć, gdzie byłoby to przydatne.
AKTUALIZACJA 2
Jednym z przykładów interfejsu API .NET, który akceptuje SecureString, jest ten konstruktor dla klasy X509Certificate . Jeśli spelunkujesz z ILSpy lub podobnym, zobaczysz, że SecureString jest wewnętrznie konwertowany na niezarządzany bufor (
Marshal.SecureStringToGlobalAllocUnicode
), który jest następnie zerowany po zakończeniu z (Marshal.ZeroFreeGlobalAllocUnicode
).źródło
SecureString
danych wejściowych, jakie byłoby ich zastosowanie? Zawsze będziesz musiał wrócić dostring
wcześniejszego lub późniejszego (lubBSTR
jak wspomniałeś, co nie wydaje się bardziej bezpieczne). Po co więcSecureString
w ogóle zawracać sobie głowę ?Microsoft nie zaleca używania
SecureString
nowszych kodów.Z dokumentacji klasy SecureString :
Który zaleca:
źródło
Jak prawidłowo zidentyfikowałeś,
SecureString
oferuje jedną konkretną przewagę nadstring
: usuwanie deterministyczne. Istnieją dwa problemy z tym faktem:SecureString
. Oznacza to, że trzeba być ostrożnym, aby nie stworzyć GC zarządzane niezmiennestring
lub dowolny inny bufor, który będzie przechowywany poufnych informacji (lub będziesz musiał śledzić że również). W praktyce nie zawsze jest to łatwe do osiągnięcia, ponieważ wiele interfejsów API oferuje tylko sposób pracystring
, a nieSecureString
. A nawet jeśli wszystko zrobisz dobrze ...SecureString
chroni przed bardzo szczególnymi rodzajami ataków (a dla niektórych z nich nie jest nawet tak niezawodny). Na przykładSecureString
pozwala zmniejszyć okno czasowe, w którym osoba atakująca może zrzucić pamięć procesu i pomyślnie wyodrębnić poufne informacje (ponownie, jak prawidłowo wskazano), ale ma nadzieję, że okno jest zbyt małe, aby atakujący mógł wykonanie migawki pamięci w ogóle nie jest uważane za bezpieczeństwo.Kiedy więc go użyć? Tylko wtedy, gdy pracujesz z czymś, co pozwala ci pracować
SecureString
dla wszystkich twoich potrzeb, a nawet wtedy powinieneś pamiętać, że jest to bezpieczne tylko w określonych okolicznościach.źródło
Poniższy tekst został skopiowany z analizatora kodów statycznych HP Fortify
Streszczenie: Metoda PassString () w PassGenerator.cs przechowuje poufne dane w niepewny sposób (tj. W postaci ciągu), umożliwiając wyodrębnienie danych poprzez sprawdzenie sterty.
Wyjaśnienie: Wrażliwe dane (takie jak hasła, numery ubezpieczenia społecznego, numery kart kredytowych itp.) Przechowywane w pamięci mogą być wyciekane, jeśli są przechowywane w zarządzanym obiekcie String. Obiekty łańcuchowe nie są przypięte, więc moduł odśmiecający może dowolnie przenieść te obiekty i pozostawić kilka kopii w pamięci. Obiekty te nie są domyślnie szyfrowane, więc każdy, kto może odczytać pamięć procesu, będzie mógł zobaczyć zawartość. Ponadto, jeśli pamięć procesu zostanie zamieniona na dysk, niezaszyfrowana zawartość ciągu zostanie zapisana w pliku wymiany. Wreszcie, ponieważ obiekty String są niezmienne, usunięcie wartości String z pamięci może być wykonane tylko przez moduł odśmiecający CLR. Odśmiecanie nie jest wymagane do uruchomienia, chyba że CLR ma mało pamięci, więc nie ma gwarancji, kiedy nastąpi odśmiecanie.
Zalecenia: Zamiast przechowywać wrażliwe dane w obiektach takich jak Strings, przechowuj je w obiekcie SecureString. Każdy obiekt przez cały czas przechowuje w pamięci zaszyfrowaną zawartość.
źródło
Chciałbym poruszyć ten punkt:
Osoba atakująca może nie mieć pełnego dostępu do komputera i aplikacji, ale może mieć dostęp do niektórych części pamięci procesu. Jest to zwykle spowodowane błędami, takimi jak przepełnienie bufora, gdy specjalnie skonstruowane dane wejściowe mogą spowodować ujawnienie lub nadpisanie części pamięci przez aplikację.
Przeciekająca pamięć HeartBleed
Weźmy na przykład Heartbleed. Specjalnie skonstruowane żądania mogą spowodować, że kod ujawni atakującemu losowe części pamięci procesu. Atakujący może wyodrębnić certyfikaty SSL z pamięci, ale jedyne, czego potrzebuje, to po prostu użyć źle sformułowanego żądania.
W świecie zarządzanego kodu przepełnienia buforów stają się problemem znacznie rzadziej. W przypadku WinForms dane są już przechowywane w niepewny sposób i nie można nic z tym zrobić. To zapewnia ochronę za pomocą
SecureString
praktycznie bezużyteczną.Można jednak zaprogramować GUI
SecureString
, a w takim przypadku warto ograniczyć okno dostępności hasła w pamięci. Na przykład PasswordBox.SecurePassword z WPF jest typuSecureString
.źródło
Jakiś czas temu musiałem stworzyć interfejs ac # przeciwko bramie płatności kartą kredytową Java i potrzebowałem kompatybilnego bezpiecznego szyfrowania klucza komunikacyjnego. Ponieważ implementacja Java była raczej specyficzna i musiałem pracować z chronionymi danymi w określony sposób.
Uważam, że ten projekt jest dość łatwy w użyciu i łatwiejszy niż praca z SecureString… dla tych, którzy lubią używać… nie krępuj się, żadnych ograniczeń prawnych :-). Pamiętaj, że te klasy są wewnętrzne, być może trzeba je upublicznić.
źródło