Preorder drugiego tomu książki sekuraka: Wprowadzenie do bezpieczeństwa IT. -15% z kodem: sekurak-book

Zabezpieczanie aplikacji w ASP.NET z poziomu konfiguracji

30 stycznia 2013, 09:41 | Teksty | 0 komentarzy

Wstęp

Poniższy artykuł rozpoczyna serię wpisów na temat istniejących zabezpieczeń dostępnych z poziomu konfiguracji aplikacji zbudowanych z wykorzystaniem platformy ASP.NET, skierowaną przede wszystkim do wdrożeniowców i pentesterów.

Wdrożeniowcy odnajdą tutaj zwięzły przewodnik po właściwych ustawieniach zabezpieczeń aplikacji na produkcji wraz z krótkim wyjaśnieniem działania każdego z mechanizmów. Pentesterzy, wykonujący przegląd konfiguracji aplikacji opartej o ASP.NET po lekturze tekstu będą w stanie stwierdzić, jakie słabości dana konfiguracja kryje.

Także programiści znajdą tutaj coś dla siebie – część z opisywanych zabezpieczeń można wprowadzić jedynie z poziomu kodu źródłowego aplikacji.

 

Wyłączenie ustawień deweloperskich

W ASP.NET wyróżniamy następujące ustawienia deweloperskie związane z bezpieczeństwem: trace, debug i customErrors.

1. trace

Mając włączony mechanizm trace aplikacja odkłada odwołania do kolejnych stron. Po wejściu w szczegóły pojedynczego żądania HTTP do konkretnego zasobu widać informacje dotyczące tego żądania  – dane przychodzące od użytkowników (m.in. ciasteczka, wartości trzymane w sesji, wartości z pól formularza), oraz informacje po stronie serwera (zmienne serwerowe, ścieżkę na dysku do konkretnej strony), które potencjalnie są przydatne atakującemu.

Aby dostać się do informacji z trace wystarczy przejść do strony Trace.axd (np. http://localhost/MyApp/Trace.axd).

Zabezpieczanie aplikacji w ASP.NET z poziomu konfiguracji

Schemat wpisu w pliku web.config:

<trace
enabled="true|false"
localOnly="true|false"
pageOutput="true|false"
requestLimit="integer"
mostRecent="true|false"
writeToDiagnosticsTrace="true|false"
traceMode="SortByTime|SortByCategory"/>

Domyślna konfiguracja:

<trace
enabled="false"
localOnly="true"
mostRecent="false"
pageOutput="false"
requestLimit="10"
traceMode="SortByTime"
writeToDiagnosticsTrace="false"/>

Możliwe jest całkowite wyłączenie mechanizmu trace poprzez ustawienie:

<trace enabled="false"/>

Poniższe ustawienie:

<trace enabled="true" localOnly="false"/>

pozwala na dostęp do informacji z trace z dowolnej maszyny. Aby pozostawić dostęp tylko z poziomu samego serwera wystarczy usunąć atrybut localOnly.

Więcej informacji na temat ustawień trace można znaleźć tutaj:
http://msdn.microsoft.com/en-us/library/ie/6915t83k.aspx

2. debug

Ustawienie flagi debug=true wpiera proces diagnozowania błędów w aplikacji. Takie ustawienie nie powinno się pojawić w produkcyjnej konfiguracji, głównie ze względu na negatywny wpływ na wydajność aplikacji. Jeżeli wystąpi i dodatkowo mechanizm custom errors jest wyłączony, to poza dokładnymi informacjami o wyjątku pojawią się też fragmenty kodu źródłowego.

Domyślna konfiguracja:

<compilation debug="false" ...>
...
</compilation>

 Poprawnie powinno być tak:

<system.web>
<compilation debug="false" ...>
...
</compilation>
</system.web>

Więcej informacji na temat ustawienia debug jest dostępnych tutaj:
http://aspnetresources.com/articles/debug_code_in_production

3. custom errors

Custom errors to mechanizm, który pozwala przy wystąpieniu błędu po stronie aplikacji przekierować użytkownika na specjalnie w tym celu przygotowaną stronę. Jeżeli przekierowanie jest nieskonfigurowane, natomiast mechanizm włączony (mode=”On”), to użytkownikowi jest wyświetlana dość lakoniczna informacja o błędzie i wiadomość o tym, że mechanizm custom errors jest włączony i co należy zrobić, aby go wyłączyć.

Przykład wyjątku z włączonym:

ASP-NET-02

i wyłączonym mechanizmem custom errors.

ASP-NET-03

W przypadku gdy mechanizm jest wyłączony, są prezentowane szczegóły wyjątku: dokładny stack trace, ścieżka na dysku do aplikacji. Aby nie straszyć użytkownika błędami oraz nie ujawniać szczegółów o nich, wystarczy z poziomu pliku web.config ustawić przykładowo:

<customErrors mode="On" />

lub:

<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx" />

jeżeli mamy specjalną stronę Error.aspx w głównym katalogu aplikacji zawierającą jedynie komunikat o tym, że wystąpił problem z aplikacją i opcjonalnie prośbę o kontakt z administratorem.

Schemat wpisu w pliku web.config:

<system.web>
<customErrors defaultRedirect="url"
mode="On|Off|RemoteOnly">
<error. . ./>
</customErrors>
</system.web>

Opcja RemoteOnly powoduje, że szczegóły błędu dostępne są jedynie na lokalnej maszynie, poza tym działa analogicznie jak On.

Domyślna konfiguracja:

<customErrors mode="RemoteOnly" />

Istnieje możliwość przekierowania na różne strony błędu w zależności od kodu odpowiedzi HTTP, przykład można znaleźć tutaj:
http://msdn.microsoft.com/en-us/library/h0hfz6fc.aspx

Więcej na temat popularnych błędów w konfiguracji:
http://weblogs.asp.net/dotnetstories/archive/2009/10/24/five-common-mistakes-in-the-web-config-file.aspx

 

Konfiguracja ciasteczek

Schemat konfiguracji ciasteczek w pliku web.config:

<httpCookies domain="String"
httpOnlyCookies="true|false"
requireSSL="true|false" />

Domyślna konfiguracja:

<httpCookies httpOnlyCookies="false"
requireSSL="false"
domain="" />

Aby zabezpieczyć ciasteczka przesyłane przez naszą aplikację należy rozważyć nadanie im następujących flag: httpOnly i secure.

1. flaga httpOnly

Oznaczenie ciasteczka flagą httpOnly sprawia, że dane ciasteczko nie będzie dostępne poprzez odwołanie z poziomu skryptów po stronie klienta (Javascript, VBScript). Pozwala to zabezpieczyć aplikację np. przed przejęciem ciasteczka z identyfikatorem sesji użytkownika przez atakującego przy wykorzystaniu podatności typu XSS.
Więcej na ten temat tutaj: http://msdn.microsoft.com/en-us/library/ms533046.aspx

Aby to osiągnąć wystarczy odpowiedni wpis w pliku web.config:

<system.web>
<httpCookies httpOnlyCookies="true">
</system.web>

Przykład nagłówka odpowiedzi HTTP ustawiający ciasteczko z flagą httpOnly (google.pl):

--------------------------------
....
Set-Cookie: NID=577=Pav5HNGaDlTGcMaWPMjA1WiBOQADwXtq4b4-S2yT0XtsIA2_OouKx-FhDRTGH2pLTyJwHo3UkihWROKrgEkDp9BqIospmq5GGeI60BkGBU9u2BsxSNOLWaL6Ml457D; expires=Tue, 14-Aug-2012 09:58:16 GMT; path=/; domain=.google.pl; HttpOnly
....
--------------------------------

 2. flaga secure

Atrybut secure w nagłówku Set-Cookie według specyfikacji oznacza, że klient (przeglądarka) powinien skorzystać z bezpiecznego sposobu przesyłania go z powrotem na serwer. W praktyce oznacza to, że wysyłany jest przez przeglądarkę tylko z wykorzystaniem SSL. Aby skonfigurować aplikację tak, żeby dodawała ten atrybut należy ustawić opcję requireSSL.

Część stron wykorzystuje https tylko do logowania użytkownika, natomiast po poprawnym zalogowaniu przechodzi na http.
W przypadku przesyłania cookie sesji takiej strony (nie ma atrybutu secure) poprez sieć niezaufaną, np. niezabezpieczone WIFI w kawiarni, istnieje możliwość, że zostanie ono wykradzione i wykorzystane do podszycia się pod użytkownika. Świetnym przykładem jak i przestrogą jest tutaj aplikacja FireSheep (http://en.wikipedia.org/wiki/Firesheep).

<system.web>
<httpCookies requireSSL="true">
</system.web>

W przypadku korzystania z FormsAuthentication sytuacja wygląda analogicznie:

<authentication mode="Forms">
<forms requireSSL="true" ... />
</authentication>

Przykład nagłówka ustawiający ciasteczko z flagą secure (owasp.org):

--------------------------------
....
Set-Cookie: wiki_session=dkvtgi2ijsqd5lkee39r35ndhp0; path=/; secure; HttpOnly
....
--------------------------------

Więcej informacji na temat ochrony ciasteczek można znaleźć tutaj:
https://www.owasp.org/index.php/HttpOnly
http://www.codinghorror.com/blog/2008/08/protecting-your-cookies-httponly.html
http://www.dotnetnoob.com/2010/11/how-to-secure-aspnet-cookies.html
http://www.ietf.org/rfc/rfc2109.txt

 

Zabezpieczenie ViewState

Protokół HTTP jest bezstanowy, w związku z czym powstał szereg mechanizmów, który pozwala w jakiś sposób zasymulować stan w komunikacji po HTTP – w przypadku ASP.NET są to m.in. mechanizm ViewState i mechanizm sesji. Mechanizm sesji pozwala przechowywać zmienne związane z danym użytkownikiem po stronie serwera. Obiekt sesji jest łączony z konkretnym użytkownikiem poprzez odpowiednie ciasteczko, w związku z czym dostęp do zmiennych w sesji jest możliwy z różnych stron naszej aplikacji.

ViewState pozwala natomiast na przechowanie stanu elementów danej strony aspx tylko pomiędzy kolejnymi jej postback’ami. Postback oznacza wysłanie formularza z przeglądarki na serwer, który jako wynik zwraca tą samą stronę. W odróżnieniu od sesji, wartość ViewState jest przesyłana cały czas pomiędzy przeglądarką a serwerem.

ViewState w uproszczeniu to kolekcja par klucz wartość, ViewState formularza jest sumą kolekcji ViewState kontrolek w hierarchii formularza.

Mechanizm ViewState realizowany jest domyślnie poprzez ukryte pole formularza o nazwie __VIEWSTATE, którego wartość jest po stronie serwera serializowana z wykorzystaniem klasy LosFormatter (limited object serialization) oraz kodowana z wykorzystaniem Base64.
Aby zobaczyć jak wygląda Viewstate, z poziomu prostej strony Default.aspx do Viewstate dodajemy jedną wartość (z poziomu code behind):

ViewState["secret"] = "my presious"

Po otwarciu strony w przeglądarce mamy w kodzie html strony:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUENTM4MQ8WAh4Gc2VjcmV0BQtteSBwcmVzaW91c2Rk" />

Po zrobieniu Base64 decode otrzymujemy:

ASP-NET-04

Narzędzie Burp Suite pozwala zobaczyć sparsowany ViewState, gdzie widzimy dokładnie nasz element dodany z kodu. Dodatkowo mamy informację, że MAC nie jest włączone, ale to tym za moment.

ASP-NET-05

Zabezpieczenie ViewState przed możliwością modyfikacji jest istotne wtedy, gdy znajdują się w nim wartości którymi użytkownik nie powinien manipulować. ViewState niezabezpieczony może zostać przeparsowany, a interesująca atakującego wartość łatwo zmodyfikowana. Aby zabezpieczyć się przed tym należy skorzystać z opcji enableViewStateMac, natomiast jeżeli chcemy dodatkowo ukryć samą treść ViewState przed możliwością odczytania, korzystamy z szyfrowania ViewState – ustawienia viewStateEncryptionMode.

Schemat konfiguracji :

<pages
...
enableViewStateMac="[True|False]"
viewStateEncryptionMode="[Always|Auto|Never]"
...
>

Domyślna konfiguracja:

<pages
...
enableViewStateMac="true"
viewStateEncryptionMode="Auto"
...
</pages>

1. enableViewStateMac

Opcja enableViewStateMac ustawiona na true powoduje, że dla ViewState’u wyliczany jest MAC (Machine Authentication Code), a następnie dołączany na jego końcu. Kiedy wartość ViewState jest przez przeglądarkę odsyłana na serwer, ASP.NET ponownie wylicza MAC i jeżeli MAC przysłany razem z ViewState i nowo wyliczony się różnią, otrzymana wartość ViewState nie jest brana pod uwagę. Pozwala to zapobiec próbom modyfikacji wartości ViewState przez potencjalnego atakującego.

Aby ASP.NET dołączał i sprawdzał MAC dla ViewState należy ustawić w pliku web.config:

<system.web>
<pages
...
enableViewStateMac="true"
...
</pages>
</system.web>

Przykład z początku paragrafu z enableViewStateMac=”true” :

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUENTM4MQ8WAh4Gc2VjcmV0BQtteSBwcmVzaW91c2Rk3tR+yg6/2maxiEtvzxcAidrUCWjUZRV2vx+uounE27g=" />

Zdekodowany:

ASP-NET-06

Sprasowany:

ASP-NET-07

Możemy co prawda nadal zobaczyć, co się znajduje w kolekcji ViewState, natomiast jego modyfikacja w sytuacji, gdy mamy np. formularz przy jego submicie spowoduje wystąpienie wyjątku typu ViewStateException.

2. viewStateEncryptionMode

Ustawienie viewStateEncryptionMode określa sytuacje kiedy ViewState jest szyfrowany:
# Always – zawsze
# Never – nigdy
# Auto – jest szyfrowany, jeżeli kontrolka tego zażąda (poprzez wywołanie metody Page.RegisterRequiresViewStateEncryption())

W celu włączenia szyfrowania należy w pliku web.config ustawić:

<system.web>
<pages
...
viewStateEncryptionMode="Always"
...
</pages>
</system.web>

Nasz przykład z włączonym viewStateEncryptionMode=”Always” :

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="lWAQeD9//qD9i/ux3HT2VKHxY2pchUPv0Aj5Kj9K25IZ1oO6EFISlot8xLcC3LA4VH+CDrFJrG8wqXjCdoUy/Xg2LF79ffn/lmv2+xApJyT5OwmbnqcXVPltYXVpwZ7u0gtWW6j+8tQSB44sDVT66Q==" />

Próba zrobienia Base64 decode:

ASP-NET-08

Próba sparsowania:

ASP-NET-09

Teraz mamy ViewState w formie całkowicie nieczytelnej.

ViewState jest szyfrowany z wykorzystaniem algorytmu skonfigurowanego w ustawieniu validation w sekcji machineKey, z wykorzystaniem klucza ustawionego we właściwości decryptionKey. Podczas liczenia MAC dla ViewState wykorzystywany jest klucz z opcji validationKey. Przykładowo, jeżeli jako validation będzie wybrany algorytm AES, to zostanie użyty do szyfrowania ViewState, natomiast do walidacji ViewState (MAC) zostanie wykorzystany HMACSHA1.

Fragment schematu konfiguracji:

<system.web>
<machineKey
...
validationKey="AutoGenerate|value[,IsolateApps]"
decryptionKey="AutoGenerate|value[,IsolateApps]"
validation="HMACSHA256" [SHA1 | MD5 | 3DES | AES | HMACSHA256 | HMACSHA384 | HMACSHA512 | alg:algorithm_name]
...
/>
</system.web>

 

Zarówno validationKey jak i decyptionKey mogą być generowane losowo, jak i możemy, bezpośrednio podać wartość. Dodatkowo, przy generacji podając opcję IsolateApps klucze będą generowane per aplikacja.

Istnieje jeszcze możliwość zabezpieczenia ViewState w taki sposób, aby był inaczej szyfrowany dla różnych użytkowników. Natomiast to jest do zrealizowania tylko z poziomu kodu aplikacji (wykorzystanie właściwości Page.ViewStateUserKey).

Ustawienie to pozwala zabezpieczyć aplikację przed atakami typu Cross Site Request Forgery. Problematyka ataku CSRF poruszona jest szerzej tutaj:
http://www.troyhunt.com/2010/11/owasp-top-10-for-net-developers-part-5.html

Więcej informacji na temat ViewState i konfiguracji machineKey:
http://msdn.microsoft.com/en-us/library/ms972976.aspx
http://msdn.microsoft.com/en-us/library/ms178199(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/950xf363.aspx
http://aspnetresources.com/articles/ViewState
http://msdn.microsoft.com/en-us/library/ff649308.aspx
http://msdn.microsoft.com/en-us/library/w8h3skw9.aspx
http://msdn.microsoft.com/en-us/library/bb386448.aspx

 

Ukrywanie nagłówków odpowiedzi HTTP charakterystycznych dla ASP.NET

Poniższy przykład przedstawia najczęściej występujące nagłówki odpowiedzi HTTP przesyłane przez aplikacje stworzone z wykorzystaniem ASP.NET:

----------------------------
HTTP/1.1 200 OK
...
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
X-AspNetMvc-Version: 1.0
-----------------------------

Są to nagłówki serwera IIS, oraz nagłówki związane z samym ASP.NET. Nagłówki te same w sobie stanowią bardziej narzut na ruch sieciowy niż zagrożenie jako takie, niemniej jednak ujawniają informacje na temat wykorzystywanych wersji serwera IIS czy ASP.NET, więc warto zadbać o to aby nasza aplikacja ich nie wysyłała. Poniżej są zaprezentowane sposoby usunięcia każdego z nich.

1. X-AspNet-Version

Aby pozbyć się tego nagłówka należy dodać w pliku web.config następujący wpis:

<system.web>
<httpRuntime enableVersionHeader="false" />
</system.web>

 2. X-AspNetMvc-Version

Ten nagłówek oznacza wersję ASP.NET MVC z jakiej korzysta nasza aplikacja. W przypadku tego nagłówka nie ma możliwości wycięcia go na poziomie konfiguracji, można to zrobić tylko z poziomu kodu aplikacji.

Aby tego dokonać należy z poziomu Global.asax dodać w Application_Start linijkę:

MvcHandler.DisableMvcResponseHeader = true;

3. X-Powered-By

Z poziomu IIS Managera wchodzimy we właściwości strony i znajdujemy ustawienie: Nagłówki odpowiedzi HTTP, a następnie usuwamy go z listy.

ASP-NET-10

4. Server

Aby ten nagłówek usunąć konieczne są dwie zmiany. Pierwsza – zainstalowanie narzędzia UrlScan i skonfigurowanie w pliku UrlScan.ini opcji RemoveServerHeader=1. Więcej na temat narzędzia UrlScan nieco później.

W przypadku poprawnych  żądań do strony to wystarczy aby ukryć nagłówek Server, natomiast gdy do aplikacji zostanie wysłane nieprawidłowe żądanie (zwracające w odpowiedzi 400 BadRequest), to pojawi się nam ponownie nagłówek Server, tym razem o wartości Microsoft-HTTPAPI/2.0.

---------------------------------------------
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 13 Feb 2012 22:05:19 GMT
Connection: close
Content-Length: 300
---------------------------------------------

Aby i ten nagłówek się nie pojawił trzeba wyedytować klucz w rejestrze:

HKLM\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\DisableServerHeader

nadając mu wartość 1.

Fragment odpowiedzi zwracającej kod 400 przy modyfikacji rejestru:

-------------------------------------------
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=us-ascii
Date: Mon, 13 Feb 2012 21:51:41 GMT
Connection: close
Content-Length: 300
...
------------------------------------------

Ukrycie nagłówków można też osiągnąć w sposób programistyczny, opis dostępny tutaj:
http://consultingblogs.emc.com/howardvanrooijen/archive/2009/08/25/cloaking-your-asp-net-mvc-web-application-on-iis-7.aspx

Więcej informacji na temat tego, czemu warto je ukryć można znaleźć tutaj:
http://www.troyhunt.com/2012/02/shhh-dont-let-your-response-headers.html

 

Checklista

  1. Wyłącz ustawienia deweloperskie – trace (enabled=”true”), debug (debug=”false”)
  2. Włącz mechanizm custom errors (mode=”On” lub „RemoteOnly”)
  3. Sprawdź, czy:
    1. ciasteczka nie potrzebne z poziomu skryptów mają ustawione httpOnly (httpOnlyCookies=”true”),
    2. przy wykorzystaniu https mają ustawione requireSSL=”true”
  4. Włącz szyfrowanie ViewState (viewStateEncryptionMode=”Always”) i opcję dołączania do ViewState MAC (enableViewStateMac=”true”)
  5. Ukryj nagłówki charakterystyczne dla ASP.NET i serwera IIS

 

Podsumowanie

W artykule omówiliśmy ustawienia deweloperskie, które powinny być wyłączone w produkcyjnej konfiguracji aplikacji, później omówiliśmy zabezpieczenie danych trzymanych w ViewState, opisaliśmy także ciasteczka charakterystyczne dla ASP.NET i sposoby na ich wyłączenie. W kolejnej części przejdziemy m.in. do ukrycia informacji na temat usług sieciowych i serwisów WCF, poznamy także dokładniej możliwości narzędzia UrlScan.

Spodobał Ci się wpis? Podziel się nim ze znajomymi:



Komentarze

Odpowiedz