Sekcje

Przejdź na skróty do treści. | Przejdź do nawigacji


Baza wiedzy Publikacje Zabezpieczanie aplikacji w ASP.NET z poziomu konfiguracji

Zabezpieczanie aplikacji w ASP.NET z poziomu konfiguracji

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.

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).

sample trace

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

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

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:
custom errors on

i wyłączonym mechanizmem custom errors.

custom errors off
 
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 wspisu 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.

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
....
--------------------------------

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:

viewstate 1

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.

viewstate 2

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>

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:

viewstate 3

Sprasowany:

viewstate 4

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.

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:

viewstate 5

Próba sparsowania:

viewstate 6

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.

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> 

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;

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.

x-powered-by

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:
    a. ciasteczka nie potrzebne z poziomu skryptów mają ustawione httpOnly (httpOnlyCookies="true"),
    b. 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.

Przydatne informacje? Polub nas na facebooku.

Nawigacja
 
Darmowy magazyn o ITsec

zine
Subskrybuj RSS:
RSS