Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

SecureString a kod natywny

Jednym z wyzwań przed jakimi stoją programiści w trakcie pracy nad aplikacją jest sposób przechowywania w pamięci ‘wrażliwych danych’, czyli wszelkiego rodzaju haseł, connection stringów, etc., a zatem informacji, którymi ludzie niechętnie dzielą się ze wszystkimi wokół. Generalnie chodzi bowiem o to, żeby jakiś dobry człowiek podczas przeglądania pamięci procesu nie miał na widelcu wszystkich tych danych, a jedynie sieczkę z której wyciągnięcie czegokolwiek sensownego graniczy z cudem. I właśnie specjalnie na takie potrzeby w ramach .NET Framework pojawił się kilka lat temu SecureString - klasa, która pozwala na bezpieczne przechowywanie łańcuchów w pamięci. Nie jest bowiem żadną tajemnicą, że raz ustalona wartość klasycznego stringa żyje sobie dosyć długo w pamięci, a dodając do tego fakt, że string jest ‘immutable’, to każde przypisanie nowej wartości powoduje zaalokowanie nowego obszaru w pamięci, pozostawiając oczywiście dla GC poprzednią wartość. Zilustrujemy to przykładowym kodem.

using System;
using System.Security;
using System.Text;
namespace pl.net.zine.Articles.StringMemory
{
 public class Program
 {
    public static void Main()
    {
     Program p = new Program();
     p.Run1();
     p.Run2();
    p.Run3();
    }
    public void Run1()
    {
     string text = "Joanna ";
     for (int i = 0; i < 5; i++)
     {
       text += i;
     }
    }
    public void Run2()
    {
     System.Text.StringBuilder sb = new System.Text.StringBuilder();
     sb.Append('M');
     sb.Append('i');
     sb.Append('k');
     sb.Append('o');
     sb.Append('l');
     sb.Append('a');
     sb.Append('j');
     sb.Append(' ');
    }
    public void Run3()
    {
     System.Security.SecureString ss = new System.Security.SecureString();
     ss.AppendChar('W');
     ss.AppendChar('o');
     ss.AppendChar('j');
     ss.AppendChar('t');
     ss.AppendChar('e');
     ss.AppendChar('k');
     ss.AppendChar(' ');
     Console.WriteLine("What do we have in memory?");
     Console.ReadKey();
    }
 }
}

Uruchamiamy windbg, ładujemy naszą przykładową aplikację, pozwalamy na kontynuację, po czym w momencie pojawienia się pytania "What do we have?" wciskamy ctrl+c i lądujemy w debuggerze.

0:003> .loadby sos mscorwks
0:003> .load sosex
0:003> !strings -m:Joanna*
Address   Gen  Value
---------------------------------------
01449000   0   Joanna
01449d38   0   Joanna 0
01449d7c   0   Joanna 01
01449dc0   0   Joanna 012
01449e08   0   Joanna 0123
01449e50   0   Joanna 01234
---------------------------------------
6 matching strings
0:003> !strings -m:Mikolaj*
Address   Gen  Value
---------------------------------------
01449e90   0   Mikolaj
---------------------------------------
1 matching string
0:003> !strings -m:Wojtek*
0 matching strings

No cóż... jak widać, pomimo tego, że wyszliśmy już z zasięgu metody Run1 (zatrzymaliśmy się w Run3!) w której utworzyliśmy zmienną text i sukcesywnie dodawaliśmy do niej kolejne liczby, to wszystkie stringi zachowały się w nienaruszonym stanie. Dodatkowo, gołym okiem widać, że instrukcja ala += i spowodowała utworzenie nowego obiektu i skopiowanie do niego nowej zawartości.
StringBuilder z Run2 co prawda nie tworzył kopii obiektów, jednak jego zawartość również nie ukrywa się przed nami w żaden sposób i obnaża swoją zawartość w punkcie zatrzymania.
I na koniec okazuje się, że zawartość obiektu typu SecureString nie jest dostępna bezpośrednio i proste przeszukiwanie pamięci nie da pozytywnych rezultatów, co jak najbardziej jest zgodne z naszymi oczekiwaniami. Do przejrzenia pamięci skorzystałem z biblioteki sosex.dll, a właściwie dostępnego w niej rozszerzenia strings.

W sumie wszystko jest ok - używajmy zatem SecureString, a nasze dane będą bezpieczne! Przynajmniej, nie ma ich w pamięci.

SecureString nie jest jednak najwygodniejszą klasą do codziennego użytku, a i Framework nie dostarcza nam za wiele miejsc, w których klasa ta byłaby wykorzystywana. Jednym z takich miejsc, gdzie SecureString znalazł swoje miejsce jest metoda Start klasy Process, która pozwala na uruchomienie nowego procesu w kontekście innego użytkownika, którego login i hasło przekazywane są w postaci parametrów. I właśnie hasło można przekazać w postaci obiektu typu SecureString, co wydaje się dla nas idealne: koniec z przekazywaniem wartości parametrów w postaci jawnego tekstu! Jak to napisał Sławek w swoim ostatnim tekście odnośnie parametrów opcjonalnych metod, gdzie martwi się o wrzucanie na stos 'gotowych' wartości:

"Warto jednak jest mieć ten fakt na uwadze, choćby z tego powodu, że gdzieś w pamięci zduplikowane zostaną dane przekazane do metody. A to nie zawsze może być pożądane."

I faktycznie, chyba nie chcielibyśmy zobaczyć na stosie naszego skrzętnie skrywanego hasełka w postaci jawnego tekstu. Zobaczmy zatem, co w trawie piszczy. Użyjemy do tego celu fragmentu kodu z książki Richtera 'CLR via C#', w którym względnie bezpiecznie przygotowujemy hasełko (zwraca uwagę użycie 'using', co jeszcze bardziej zabezpiecza nasze dane), a następnie przekazujemy je do metody tworzącej nowy proces.

using System;
using System.Security;
namespace pl.net.zine.Articles.SecureStr
{
 public static class Program
 {
    public static void Main()
    {
     using (SecureString ss = new SecureString())
     {
       Console.Write("Please enter password: ");
       while (true)
       {
         ConsoleKeyInfo cki = Console.ReadKey(true);
         if (cki.Key == ConsoleKey.Enter) { Console.WriteLine(); break; }
         // Append password characters into the SecureString
         ss.AppendChar(cki.KeyChar);
         Console.Write("*");
       }
       Console.WriteLine("Creating process!");
       Console.ReadKey();
       CreateSecureProcess(ss);
     }
    }
    private static void CreateSecureProcess(SecureString pass)
    {
     System.Diagnostics.Process.Start("notepad.exe", "mgrzeg", pass, "zine.net");
    }
 }
}

Jak widać, spróbujemy uruchomić notatnik jako użytkownik ‘mgrzeg’ z domeny ‘zine.net’ i hasełkiem wprowadzonym z klawiatury.
I znowu, ładujemy WinDbg, ładujemy program, pozwalamy na jego działanie, po czym zatrzymując się na ReadKey ustawiamy malutki breakpoincik :)

0:003> bp advapi32!CreateProcessWithLogonW
0:003> g
ModLoad: 7a440000 7abc5000   C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\System\3de5bd01124463d7862bd173af90bc83\System.ni.dll
Breakpoint 0 hit
eax=00000080 ebx=00193080 ecx=79f9719c edx=00000002 esi=0012f2ac edi=0012f688
eip=77e05ffd esp=0012f1f4 ebp=0012f268 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ADVAPI32!CreateProcessWithLogonW:
77e05ffd 689c070000      push    offset <Unloaded_32.dll>+0x79b (0000079c)
0:000> kb
ChildEBP RetAddr  Args to Child             
0012f1f0 79e71ca7 0144aba8 0144abc8 001b73c0 ADVAPI32!CreateProcessWithLogonW

Chwilę później znowuż lądujemy w debuggerze i możemy sprawdzić zawartość stosu. Zrzućmy zatem kilka pierwszych parametrów funkcji, która ma następujące parametry:
BOOL CreateProcessWithLogonW (
LPCWSTR lpUsername,
LPCWSTR lpDomain,
LPCWSTR lpPassword,
DWORD dwLogonFlags,
LPCWSTR lpApplicationName,
LPWSTR lpCommandLine,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCWSTR lpCurrentDirectory,
LPSTARTUPINFOW lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInfo
);

0:000> dpu @esp+4 l6
0012f1f8  0144aba8 "mgrzeg"
0012f1fc  0144abc8 "zine.net"
0012f200  001b73c0 "TajemnePaselko"
0012f204  00000000
0012f208  00000000
0012f20c  001bb358 ""notepad.exe""

Jak widać, dopasowanie odpowiednich wartości do poszczególnych parametrów nie powinno sprawiać większych kłopotów...

Niestety, okazuje się, że wszędzie tam, gdzie mamy do czynienia z punktem styku z kodem natywnym i wywoływaniem funkcji z bibliotek systemowych jesteśmy skazani na takie właśnie problemy, czyli miejsca, gdzie nasze dane nagle trafiają na stos w postaci rozwikłanej i nie jesteśmy w stanie się przed tym w żaden sposób ochronić. To oczywiście i tak o niebo lepiej, niż używanie zwykłego stringa, który leży nagutki w pamięci i nic mu po tym, że świeci golizną.

Jakkolwiek WinDbg sprawuje się bardzo dobrze, w tym miejscu chciałbym polecić znacznie przyjaźniejsze narzędzie, które zrobi za nas dokładnie to samo, jednak sprawniej i dużo przyjemniej. Chodzi mi mianowicie o Api Monitor, dostępny za darmo do ściągniecia ze strony http://www.rohitab.com/ (na potrzeby tego tekstu używam wersji v.2 alpha-r5).

Api Monitor & CreateProcess

Do osiągnięcia stanu widocznego na załączonej ilustracji wystarczyło, że po uruchomieniu i załadowaniu definicji wyszukałem wszystkie wystąpienia ‘CreateProcess’, zaznaczyłem je w ‘API Capture Filter’, podłączyłem się w odpowiednim momencie do procesu i go! :)

Wrócimy jeszcze do ApiMona w przyszłości, jednak już teraz widać siłę, jaka w nim drzemie. Polecam zabawę nim oraz dzielenie się swoimi uwagami :)

Opublikowane 1 marca 2011 21:11 przez mgrzeg
Filed under: , ,

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za publikację - Trackback z dotnetomaniak.pl

marca 2, 2011 00:48
 

MSM said:

Dzięki za Api Mirror, wygląda na świetne narzędzie ;)

Lubię Twój blog właśnie za to że wyrywasz się z zaklętego kręgu .NET-owej abstrakcji i sprawdzasz (oraz ofc pokazujesz) jak to dokładnie działa.

Byle tak dalej :)

marca 3, 2011 15:05
 

mgrzeg said:

@MSM: Dzięki za dobre słowo, dobry człowieku :) Z ciekawości podpytam: od dawna tu zaglądasz?

Niektóre z tych tekstów chodzą za mną po kilka lat i jakoś wcześniej nie mogłem się do nich zabrać: miałem cichą nadzieję, że ktoś inny je napisze i w ten sposób 'zostanę zwolniony', albo może temat okaże się nieaktualny... ale jakoś nic się nie wydarzyło. Mam też ochotę na parę przykładów z rev-eng, ale siakoś tak nie bardzo wiem od czego zacząć i komu to pokazać, żeby draki nie było ;)

Odnoszę też wrażenie, że nie mam zbyt wielu czytelników i ludzi generalnie taka tematyka 'internalsowa' nie interesuje. No cóż - jest to poniekąd zrozumiałe - ludzie zarabiają kasę na programach, które coś robią, a nie na wiedzy 'jak to działa', więc szukają wzorców i przykładów do wykorzystania na swoim podwórku.

marca 4, 2011 01:21
 

Paweł Łukasik said:

@mgrzeg: Tematyka taka jest niszowa i nie ma co ukrywać, że wiele osób będzie zaglądać tutaj z tego powodu. Pytanie tylko na jakich czytelnikach ci zależy. Bardziej technicznych czy początkujących. Oczywiście zawsze możesz przeplatać posty bardzo techniczne z tymi mniej i spróbować przyciągnąć większe ilości użytkowników. Wszystko zależy od tego jak się czujesz w tematach luźniejszych :)

Paweł

marca 7, 2011 07:53
 

mgrzeg said:

@Paweł Łukasik: a ja myślałem, że na razie jest lajtowo i 'luźniej' ;) Ciekawe ile osób będzie tu zaglądać jak pogrzebię głębiej w mechanizmach synchronizacji CLR, tudzież w bezpieczeństwie DPAPI :)

Nic to, jakoś to będzie :) Na razie jeszcze mam chęć coś pisać i jedyne czego mi trochę brak to 'feedback' merytoryczny. Może coś przeoczyłem, gdzieś popełniłem błąd... a może powinienem o czymś jeszcze napisać... sam nie wiem.

marca 7, 2011 21:01
 

MSM said:

Okazuje się że nie zauważyłem że ktoś mi odpowiedział - RSS nie przewidział takiej możliwości a nie wiedziałem że mój post okaże się godny takiego wywyższenia ;)

Jeśli mam być szczery, trafiłem tu niezbyt dawno - z pierwszym postem w styczniu (i zostałem, po przeczytaniu wszystkich archiwialnych postów). Wcześniej po prostu nawet nie wiedziałem że istnieje Twój blog.

Co do tematyki - zajmujesz się jednym z ciekawszych i nieoklepanych tematów (ile można czytać o wzorcach projektowych, DRY i SOLID - z czasem coraz trudniej napisać tu coś odkrywczego). To że prawie nie masz konkurencji jeszcze bardziej działa na twoją korzyść.

Czy to temat niszowy - pewnie po części tak, ale jeśli ktoś chciałby umieć coś więcej niż tylko klepanie kodu to wypadałoby chociaż trochę wiedzieć na temat 'jak to właściwie działa'. Chociaż zdań na to co odróżnia 'klepacza' od 'eksperta' są tak naprawdę miliony. :)

Feedback... Kto wie, może z czasem się pojawi.

marca 9, 2011 08:51
 

arturstan said:

@mgrzeg

"Odnoszę też wrażenie, że nie mam zbyt wielu czytelników i ludzi generalnie taka tematyka 'internalsowa' nie interesuje. No cóż - jest to poniekąd zrozumiałe - ludzie zarabiają kasę na programach, które coś robią, a nie na wiedzy 'jak to działa', więc szukają wzorców i przykładów do wykorzystania na swoim podwórku."

To o mnie :) Czytam (chodź jak widzisz z dużym opóźnieniem) i temat mnie wciąga. Sam nigdy się nie zapuszczam w takie rejony, ale poczytać - bardzo przyjemnie.

marca 18, 2011 15:11
 

bori said:

Niektorzy, trafiaja tu z opoznieniem. Tematyka super, sadzilem ze tylko ja sie tym interesuje, jak to naprawde dziala. Nie obnizaj poziomu, naprawde jest wystarczajaco duzo nudnych stron dla tych co maja mala wiedze.

stycznia 14, 2012 19:24
 

mgrzeg said:

@bori: dzięki :). Postaram się nie obniżać poziomu i zapraszam do lektury pozostałych wpisów oraz czytania na bieżąco :)

stycznia 16, 2012 14:45

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems