2006 09 Data Protection API i NET Framework 2 0 [Inzynieria Oprogramowania]

background image

58

Inżynieria

oprogramowania

www.sdjournal.org

Software Developer’s Journal 9/2006

Data Protection API

i .NET Framework 2.0

B

rzmi groźnie? Bo jest groźnie kiedy firmy two-

rzą oprogramowanie przechowujące pouf-

ne informacje o znaczeniu krytycznym w nie-

zaszyfrowanej postaci, klucze i hasła jawnym tekstem

w pamięci, ba również w rejestrze systemowym i pli-

kach konfiguracyjnych ! Bardziej świadomi włączają

w funkcjonalność swoich aplikacji mechanizmy szy-

frujące – owszem. Cóż jednak z tego jeśli po głębszej

analizie okazuje się iż klucze użyte podczas szyfrowa-

nia odnaleźć możemy w przestrzeni adresowej działa-

jącego procesu bądź po prostu w kodzie aplikacji? Dla

średnio rozgarniętego crakera wystarczającym narzę-

dziem będzie debugger, zrzut pamięci procesu (ang.
memory dump) i nieco czasu aby ustalić algorytm szy-

frowania, dokonać ekstrakcji klucza szyfrującego a na-

stępnie rozszyfrować informację. Poziom bezpieczeń-

stwa wzrasta gdy aplikacja do wygenerowania klucza

szyfrującego używa hasła wprowadzanego każdorazo-

wo przez użytkownika. Takie rozwiązanie nie zawsze

jest efektywne. Poza tym powstaje możliwość ataku

mającego na celu przejęcie wprowadzanego hasła –

za pomocą tych samych technik o których wspomnia-

no wcześniej. Jeśli atak taki się powiedzie a użytkownik

stosował dane hasło również do ochrony innych zaso-

bów – skutki mogą być smutniejsze niż osiołek z „Przy-

gód Kubusia Puchatka”.

Mocy przybywaj!

Moc w postaci Data Protection API przybyła dość

dawno temu bo już razem z systemem operacyjnym

Windows 2000. DPAPI rozszerzone zostało w kolej-

nych wersjach Windows lecz dostęp z poziomu plat-

formy .NET możliwy był jedynie poprzez międzyplat-

formowe (PInvoke) wywołania kodu niezarządzane-

go. Sytuację odmieniło nadejście nowej wersji .NET
Framework 2.0
posiadającej już wsparcie dla me-

chanizmu DPAPI. W przestrzeni nazw

System.Secu-

rity.Cryptography

pojawiły się dwie nowe klasy:

Pro-

tectedData

oraz

ProtectedMemory

i stała się światłość.

Ale co to jest…

Zaczynajmy zatem od początku. DPAPI jest interfej-

sem programistycznym umożliwiającym bezpiecz-

ne szyfrowanie oraz odszyfrowanie danych w oparciu

o algorytm szyfrujący wykorzystujący klucz symetrycz-

ny. Użycie takiego algorytmu oznacza iż do zaszyfro-

wania i odszyfrowania danego bloku danych służy ten

sam klucz. To z kolei oznacza iż zaszyfrowana informa-

cja będzie bezpieczna dopóki klucz nie zostanie pozna-

ny przez osoby niepowołane. Tak więc warunkiem za-

chowania bezpieczeństwa jest dobre zabezpieczenie

kluczy – i głównie to właśnie robi za nas DPAPI. Klu-

cze zabezpieczane są poprzez ich zaszyfrowanie algo-

rytmem opartym o hasło użytkownika. Domyślnie wy-

korzystywane jest w tym celu hasło użytkownika sys-

temu Windows z czego jasno wynika że DPAPI służy

do zabezpieczania lokalnych danych użytkownika da-

nej stacji roboczej, nie zaś jakiejkolwiek komunikacji po-

między użytkownikami czy komputerami w sieci. DPA-

PI nie odpowiada również za składowanie i przechowy-

wanie zaszyfrowanych informacji – zadanie to spoczy-

wa już na barkach programisty. W pewnych sytuacjach

możliwość odszyfrowania bloku danych przez inny pro-

ces tego samego użytkownika jest niepożądana, dlate-

go też aplikacja podczas szyfrowania może użyć do-

datkowego sekretnego ciągu bajtów zwanego „opcjo-

nalną entropią”.

Interakcja aplikacji użytkowej z DPAPI w maksy-

malnym skrócie wygląda następująco:

• aplikacja

„A”

uwierzytelniona w systemie jako

użytkownik

„U”

wysyła nie zaszyfrowany blok da-

nych do DPAPI;

• DPAPI szyfruje blok danych kluczem użytkowni-

ka

„U”

;

• DPAPI zwraca aplikacji

„A”

zaszyfrowany blok

który odszyfrować będą mogły tylko i wyłącznie

procesy uwierzytelnione jako użytkownik

„U”

.

DPAPI zapewnia również cykliczną wymianę kluczy na

nowe – co w przypadku sytuacji zdobycia jednego z klu-

czy przez osoby niepowołane zminimalizuje ryzyko od-

szyfrowania wszystkich poufnych danych. Standardowo

nowy klucz dla danego użytkownika generowany jest

co trzy miesiące, następnie jest zabezpieczany i skła-

Tomasz Leszczyński

Autor jest programistą z siedmioletnim doświadczeniem
zawodowym, współtwórcą grupy deweloperskiej T3
(http://www.t3.com.pl) projektującej systemy informatycz-
ne w oparciu o technologie i narzędzia firmy Microsoft.
Kontakt z autorem: leszczynski@t3.com.pl

Rysunek 1.

Schemat działania DPAPI

warstwa publiczna

warstwa prywatna

Dane do

zaszyfrowania

Dane

zaszyfrowane

CryptoAPI

APLIKACJA

DAPAPI

Local Security

Authority (LSA)

DAPAPI

background image

DPAPI i .NET Framework 2.0

Software Developer’s Journal 9/2006

dowany w profilu użytkownika. Poza ochroną, wymianą i składo-

waniem kluczy DPAPI gwarantuje iż proces szyfrujący dane i za-

rządzający kluczami będzie maksymalnie bezpieczny i odseparo-

wany od pozostałych procesów działających w systemie. Idea ta

zrealizowana została poprzez wykorzystanie usługi Local Secu-
rity Authority
(LSA). Usługa LSA jest uruchamiana podczas star-

tu systemu operacyjnego i działa przez cały czas jego pracy pod

szczególną ochroną. LSA odpowiada między innymi za uwierzy-

telnianie użytkowników, zarządzanie zabezpieczeniami systemo-

wymi i generowanie kluczy. W budowie DPAPI możemy zatem

wydzielić dwie warstwy logiczne:

• publiczną – udostępniającą interfejs dla programistów i do-

stępną poprzez CryptoAPI (

crypt32.dll

);

• prywatną – niedostępną bezpośrednio dla programistów,

realizującą właściwe zadania kryptograficzne w obrębie

chronionej usługi LSA.

Komunikacja pomiędzy warstwą publiczną a prywatną od-

bywa się poprzez wywołania RPC (Remote Procedure Call).

RPC jest techniką komunikacji międzyprocesowej (IPC – in-
terprocess communication
) uwzględniającą mechanizmy bez-

pieczeństwa (uwierzytelnianie, autoryzacja).

Jam jest klucznik

Mechanizm DPAPI opiera się o wykorzystanie trzech typów klu-

czy: głównego – MasterKey, sesyjnego – SessionKey oraz odzy-

skiwania – RecoveryKey. MasterKey jest głównym i najważniej-

szym kluczem generowanym przez DPAPI. Stanowi on najważ-

niejszą informację niezbędną do odszyfrowania bloku danych.

Klucz ten nie jest jednak jawnie wykorzystywany przez funk-

cje szyfrujące. Do bezpośredniego szyfrowania bloku danych

używany jest tzw. klucz sesyjny (ang. SessionKey) generowany

z połączenia MasterKey z losową porcją danych. Klucze Master-

Key są najwrażliwszą częścią mechanizmu dlatego też za ich

szyfrowanie, składowanie i wymianę odpowiada DPAPI.

Klucz SessionKey w przeciwieństwie do MasterKey nie jest

nigdzie zapisywany – jest wykorzystywany jednorazowo pod-

czas szyfrowania porcji danych a następnie usuwany z pamię-

ci. SessionKey powstaje poprzez policzenie skrótu SHA-1 z połą-

czenia MasterKey i 16 bajtowej losowej porcji danych. Do zaszy-

frowanego ciągu bajtów (kryptogramu) dodawana jest owa loso-

wa porcja danych która posłużyła do utworzenia klucza Session-
Key
– umożliwia ona później odtworzenie klucza sesyjnego i od-

szyfrowanie informacji. Mechanizm ten zapewnia iż każdorazo-

we zaszyfrowanie tych samych danych, tym samym kluczem
MasterKey spowoduje wygenerowanie innego kryptogramu.

Trzecim z kluczy jest asymetryczny klucz odzyskiwania

RecoveryKey. Klucz ten wykorzystywany jest do tworzenia

tzw. dysku „resetowania hasła”. W przypadku gdy użytkowniko-

wi zdarzy się zapomnieć własnego hasła może za pomocą te-

goż dysku ustanowić nowe hasło. Podczas tworzenia dysku ge-

nerowana jest para 2048-bitowych kluczy RSA: publiczny oraz

prywatny. Za pomocą klucza publicznego szyfrowane jest aktu-

alne hasło użytkownika a następnie zapisywane w profilu użyt-

kownika. Klucz prywatny natomiast jest zapisywany tylko na

dyskietce którą użytkownik powinien następnie fizycznie gdzieś

ukryć i zabezpieczyć. W momencie wprowadzenia błędnego

hasła użytkownik może wybrać opcję „resetuj”. System doko-

na próby odszyfrowania hasła zapisanego w profilu za pomocą

klucza prywatnego znajdującego się na dyskietce. Jeśli opera-

cja się powiedzie – użytkownik może ustanowić nowe hasło.

Listing 1.

Użycie klasy ProtectedData

// Dane do zaszyfrowania

byte

[]

userData

=

{

1, 2, 3, 4, 5, 6

}

;

// Dodatkowy ciąg bajtów
// (niezbędny później przy odszyfrowywaniu)

byte

[]

optionalEntropy

=

{

7, 3, 2, 9, 1

}

;

Console

.

WriteLine

(

"Przed zaszyfrowaniem: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

userData

));

// Szyfrujemy !

byte

[]

protData

=

ProtectedData

.

Protect

(

userData

,

optionalEntropy

,

DataProtectionScope

.

CurrentUser

);

Console

.

WriteLine

(

"Po zaszyfrowaniu: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

protData

));

// Odszyfrowujemy!

byte

[]

unprotData

=

ProtectedData

.

Unprotect

(

protData

,

optionalEntropy

,

DataProtectionScope

.

CurrentUser

);

Console

.

WriteLine

(

"Po odszyfrowaniu: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

unprotData

));

R

E

K

L

A

M

A

background image

60

Inżynieria

oprogramowania

www.sdjournal.org

Software Developer’s Journal 9/2006

Przepis na danie główne: MasterKey

Podstawą MasterKey jest generowany losowo ciąg 512 bitów (64

bajty) – jak już wspomniano jest on najwrażliwszą częścią me-

chanizmu ponieważ służy do generowania klucza SessionKey

którym bezpośrednio szyfrowane (i odszyfrowywane) są dane.

Jak zabezpieczany zatem jest sam MasterKey? Otóż wygenero-

wany ciąg 512 bitów szyfrowany jest algorytmem PBKDF2 opi-

sywanym w dokumencie PCKS#5 opublikowanym przez firmę

RSA. Dokument ten opisuje sposób szyfrowania danych za po-

mocą hasła oraz ochrony integralności tychże danych. W skrócie

szyfrowanie przebiega następująco: za pomocą algorytmu SHA-

1 liczony jest skrót (ang. hash) z hasła użytkownika. Następnie

wywoływana jest funkcja szyfrująca PBKDF2 do której przeka-

zywany jest skrót hasła, 16 losowo wygenerowanych bajtów sta-

nowiących tzw. modyfikator klucza (ang. salt) oraz liczba n okre-

ślająca ilość iteracji szyfrowania (domyślna wartość to 4000).

Funkcja PBKDF2 z podanych danych liczy rekurencyjnie skrót

SHA-1 przy czym ilość iteracji wynosi podane wcześniej n. Wie-

lokrotne rekurencyjne wyznaczenie skrótu znacznie utrudnia po-

tencjalny atak metodą siłową (ang. Brute-force). Wyznaczony tak

skrót jest tzw. kluczem bazującym na haśle użytkownika. W celu

zapewnienia integralności klucza MasterKey liczony jest dla nie-

go tzw. kod HMAC (Keyed-Hash Message Authentication Code).

Kod ten wyznaczany jest poprzez obliczenie skrótu SHA-1 z Ma-
sterKey
w połączeniu z hasłem użytkownika. Pozwala on na póź-

niejsze weryfikowanie integralności odszyfrowanego klucza Ma-
sterKey
czyli po prostu sprawdzenie czy jest on poprawny. Na-

stępnie za pomocą algorytmu Triple-DES wykorzystującego wy-

znaczony wcześniej klucz bazujący na haśle użytkownika szyfro-

wany jest MasterKey oraz wyliczony dla niego kod HMAC. Za-

szyfrowany MasterKey oraz kod HMAC, modyfikator klucza ba-

zującego na haśle oraz ilość iteracji użytych do wygenerowania

tego klucza zapisywane są do pliku i umieszczane w profilu użyt-

kownika. Te cztery dane w połączeniu z hasłem użytkownika po-

zwalają później na odszyfrowanie klucza MasterKey oraz spraw-

dzenie jego integralności.

Magazynier

Jak już wiemy, DPAPI przechowuje zaszyfrowane klucze Ma-
sterKey
w profilu danego użytkownika. Wiemy również iż każ-

dy MasterKey wygasa po pewnym okresie czasu po czym ge-

nerowany jest nowy klucz. Pojawia się więc pytanie w jaki spo-

sób DPAPI odszyfruje dane zaszyfrowane kluczem MasterKey

który w międzyczasie wygasł i został zastąpiony nowym. Moż-

liwe jest to dzięki temu iż DPAPI nie kasuje starych kluczy Ma-
sterKey
lecz zapisuje wszystkie w profilu użytkownika. Ponadto

każdemu kluczowi przypisywany jest unikalny identyfikator GU-

ID (Globally Unique Identifier). Ten sam numer GUID dołącza-

ny jest do kryptogramu dzięki czemu DPAPI wie którego klucza
MasterKey należy użyć dla danego kryptogramu.

Rysunek 2.

MasterKey

�����

�����������

�������������

�����������

������������
���������

������������

����

����������������

�����������������

�����������

��������������

���������

��������

������

�����������

����

������

����������

����

����������

background image

DPAPI i .NET Framework 2.0

61

www.sdjournal.org

Software Developer’s Journal 9/2006

Nasuwa się również pytanie: jeśli użytkownik zmieni hasło

to w jaki sposób DPAPI odszyfruje przechowywany MasterKey

(bieżący bądź jeden z poprzednich) który zaszyfrowany został

starym hasłem ? DPAPI rozwiązuje ten problem poprzez pró-

bę wychwycenia momentu modyfikacji hasła. Następuje wtedy

odszyfrowanie wszystkich MasterKey a następnie zaszyfrowa-

nie ich ponownie już nowym hasłem. Ponadto DPAPI zapisuje

w profilu użytkownika historię haseł użytkownika co pozwala na

ewentualne odszyfrowanie MasterKey zaszyfrowanego jednym

z poprzednich haseł.

Dla użytkowników komputerów będących członkami do-

meny DPAPI oferuje dodatkowy mechanizm wykonywania

kopii bezpieczeństwa dla generowanych MasterKey. Mia-

nowicie – po wygenerowaniu każdego MasterKey DPAPI

łączy się z kontrolerem domeny. Po nawiązaniu połączenia

otrzymuje klucz publiczny służący do szyfrowania Master-
Key
. Następnie zapisuje w profilu użytkownika dwie wer-

sje zaszyfrowanego MasterKey: pierwszą – zaszyfrowaną

sposobem standardowym za pomocą hasła użytkownika

i drugą – zaszyfrowaną kluczem publicznym otrzymanym

od kontrolera domeny. Kiedy dojdzie do sytuacji w któ-

rej DPAPI nie będzie w stanie odszyfrować danego Ma-
sterKey
połączy się z kontrolerem domeny. Po połącze-

niu przesłana zostanie kopia MasterKey zaszyfrowana klu-

czem publicznym. Kontroler za pomocą klucza prywatne-

go odszyfruje MasterKey i prześle go z powrotem do DPA-

PI. Wszystkie połączenia pomiędzy DPAPI a kontrolerem

domeny realizowane są za pomocą chronionych wywołań

RPC.

DPAPI w .NET Framework 2.0

Wraz z nadejściem .NET Framework 2.0 programiści uzyska-

li dostęp do DPAPI za pośrednictwem dwóch klas:

ProtectedDa-

ta

oraz

ProtectedMemory

. Obydwie klasy posiadają bardzo proste

interfejsy. Ich wykorzystanie sprowadza się w zasadzie do wy-

woływania dwóch statycznych metod

Protect

(szyfrowanie da-

nych) oraz

Unprotect

(odszyfrowywanie danych). Klasa

Protec-

tedData

przeznaczona jest do szyfrowania danych które będą

później gdzieś zapisywane / składowane, natomiast

Protected-

Memory

służy do szyfrowania tymczasowych danych przetrzymy-

wanych w pamięci. Różnica ta wynika m.in. ze sposobu genero-

wania klucza SessionKey. W przypadku klasy

ProtectedData

do

kryptogramu dołączane są również dodatkowe dane pozwalają-

ce odtworzyć SessionKey i odszyfrować dane. Do kryptogramu

tworzonego przez

ProtectedMemory

nie są dołączane żadne do-

datkowe dane. Dane przed zaszyfrowaniem i po zaszyfrowaniu

posiadają więc taki sam rozmiar. Dodatkowe informacje potrzeb-

ne do odtworzenia SessionKey generowane są podczas startu

systemu operacyjnego i przechowywane w pamięci przez DPA-

PI lecz nie są nigdzie zapisywane. Gdybyśmy więc zapisali kryp-

togram utworzony przez

ProtectedMemory

np. w pliku, po ponow-

nym starcie systemu operacyjnego jego odszyfrowanie byłoby

niemożliwe.

W Listingu 1 zaprezentowano bardzo prosty przykład uży-

cia klasy

ProtectedData

(należy pamiętać o wcześniejszym

dodaniu w projekcie referencji do pakietu

System.Security.dll

):

Dwa pierwsze parametry metod

Protect

i

Unprotect

nie wymagają wyjaśnienia, natomiast trzeci parametr typu

DataProtectionScope

pozwala programiście dodatkowo okre-

ślić poziom ochrony danych: dostępne tylko dla proce-

sów działających w kontekście tego samego użytkownika

(CurrentUser) lub dla wszystkich procesów działających na

danej maszynie (LocalMachine). Kolejny przykład to użycie

klasy

ProtectedMemory

:

Należy podkreślić że wielkość bloku danych który za-

mierzamy zaszyfrować powinna być wielokrotnością warto-

ści 16. Drugi parametr metody

Protect

i

Unprotect

pozwala

na określenie jednego z trzech poziomów ochrony danych:

dostępne dla wszystkich procesów (CrossProcess), dostęp-

ne tylko dla procesów działających w kontekście tego sa-

mego użytkownika (SameLogon) oraz dostępne tylko dla

procesu który zaszyfrował dane (SameProcess).

Do dzieła!

DPAPI daje nam do ręki skuteczne narzędzie do zabezpiecza-

nia poufnych informacji o znaczeniu krytycznym dla bezpieczeń-

stwa. Wraz z .NET Framework 2.0 programiści zyskali możli-

wość łatwego i bezproblemowego korzystania z tego narzędzia.

Fakt ten dodatkowo przemawia za tym aby poznać DPAPI i nie-

wielkim kosztem dokonać wielkich postępów w zakresie zwięk-

szania bezpieczeństwa tworzonych aplikacji. Należy również po-

wiedzieć otwarcie, że DPAPI nie jest złotym środkiem i reme-

dium na wszystkie problemy dotyczące bezpieczeństwa szyfro-

wanych informacji. n

W Sieci

Cennym źródłem informacji o DPAPI jest na pewno Microsoftowy
MSDN. Poniżej kilka przykładowych linków:

http://msdn.microsoft.com/msdnmag/issues/05/01/

SecurityBriefs/

http://msdn.microsoft.com/library/default.asp?url=/library/

enus/dnpag2/html/PAGHT000005.asp

http://msdn.microsoft.com/library/default.asp?url=/library/

enus/dnsecure/html/windataprotection-dpapi.asp

http://msdn.microsoft.com/msdnmag/issues/03/11/

protectyourdata/

Listing 2.

Użycie klasy ProtectedMemory

// Dane do zaszyfrowania

byte

[]

userData

=

{

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,

13, 14, 15, 16

}

;

Console

.

WriteLine

(

"Przed zaszyfrowaniem: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

userData

));

// Szyfrujemy !

ProtectedMemory

.

Protect

(

userData

,

MemoryProtectionScope

.

SameLogon

);

Console

.

WriteLine

(

"Po zaszyfrowaniu: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

userData

));

// Odszyfrowujemy !

ProtectedMemory

.

Unprotect

(

userData

,

MemoryProtectionScope

.

SameLogon

);

Console

.

WriteLine

(

"Po odszyfrowaniu: "

);

Console

.

WriteLine

(

System

.

Text

.

ASCIIEncoding

.

ASCII

.

GetChars

(

userData

));


Wyszukiwarka

Podobne podstrony:
2006 09 Wielozadaniowość w systemach operacyjnych [Inzynieria Oprogramowania]
2006 10 Łączenie kodu C z zarządzanym kodem NET [Inzynieria Oprogramowania]
2006 09 30 1451
2006 06 Wstęp do Scrum [Inzynieria Oprogramowania]
2006 09 srodi kurczace macice
2006 09 Szkoła konstruktorów klasa II
2006 09 11 DZ U 2006 169 poz 1216
Continuous real time data protection and disaster recovery
ei 2006 09 s058
Kwaśniewski J , 2006 09 16 dr kwasniewski pl, Teoretyczna długość życia Posty i głodówki
2006 10 Przegląd modeli cyklu życia oprogramowania [Inzynieria Oprogramowania]
2006 05 Antywzorce w zarządzaniu projektami informatycznymi [Inzynieria Oprogramowania]
Przykład diagramu sekwencji, Studia-WSTI (vizja.net), Inżynieria oprogramowania
ei 2006 09 s109
2006-09-24 Sejm HGW o RPO(skrót), materiały, Z PRASY
2006-09-05 Precedensowy pozew o ustalenie nieważności dekretu Bieruta, materiały, Z PRASY

więcej podobnych podstron