Artykuł opublikowany na Codeguru.pl

Z zabezpieczeniami aplikacji webowych spotykamy się prawie przy każdej okazji, gdy logujemy się do dowolnego serwisu internetowego. Dzięki temu dostajemy możliwość dostępu do różnych zasobów aplikacji WWW, które niekoniecznie są dostępne dla wszystkich użytkowników usług webowych. Wydawałoby się, że uzyskanie dostępu do zabezpieczonych części aplikacji ASP.NET wymaga autoryzacji przy każdej próbie połączenia z chronioną stroną. Na szczęście prosty mechanizm ciasteczek (ang. cookies) zwalnia nas od tej uciążliwości. Ciasteczka stanowią podstawę zabezpieczania stron ASP.NET przy użyciu mechanizmów bezpieczeństwa Forms. Są to specjalne formy HTML, które zawierają dane użytkownika (zazwyczaj nazwę użytkownika i hasło), i z których są podawane dane w celu autoryzacji zabezpieczonych zasobów aplikacji.

Zasada działania zabezpieczeń Forms

Pierwszą czynnością, jaką musimy wykonać by wykorzystać mechanizmy Forms jest ustawienie zabezpieczeń w serwerze Internetowych Usług Informacyjnych IIS (ang. Internet Information Services) na dostęp anonimowy. Dzięki temu zapytanie przechodzi przez IIS i jest bezpośrednio kierowane do ASP.NET. Tam jest rozpoznawane czy do zapytania dołączone jest ciasteczko. Jeśli ciasteczka nie ma to następuje przekierowanie do strony, na której można się zalogować. Po wpisaniu swoich danych, (zwyczajowo jest to login i hasło), i zatwierdzeniu danych następuje sprawdzenie w bazie danych prawdziwości podanych informacji. Jeśli autoryzacja wypada na naszą korzyść uzyskujemy dostęp do chronionych zasobów oraz tworzone jest ciasteczko, które mając w sobie zaszyfrowane dane, przesyłane jest do przeglądarki zalogowanego użytkownika. W przypadku, gdy nasze dane nie zostały autoryzowane pomyślnie, uzyskujemy informację o braku dostępów do zasobów i mamy możliwość ponownej próby zalogowania się do systemu. Została jeszcze jedna możliwość, a mianowicie, gdy jesteśmy już zalogowani i mamy ciasteczko. Ciasteczko jest dołączane do naszego zapytania i najpierw następuje sprawdzenie czy do zapytanie jest dołączone ciasteczko. Potem jest ono autoryzowane by następnie zostało nam przyznane zezwolenie do uzyskania zasobów i użytkownik może zobaczyć chronioną stronę. Poniżej (rys. 1), za pomocą diagramu aktywności została przedstawiona zasada korzystania z ciasteczek.

clip_image002

Rys. 1 Zasada działania mechanizmu bezpieczeństwa Forms – diagram aktywności

Przykład praktyczny

Poznaliśmy już teoretycznie jak, funkcjonuje mechanizm zabezpieczania aplikacji ASP.NET w oparciu o Forms. Do zobrazowania praktycznego wykorzystania tego typu zabezpieczeń rozszerzona zostanie prosta aplikacja, która przedstawiłem w artykule „Trójwarstwowa struktura aplikacji ASP.NET z wykorzystaniem procedur wbudowanych”. Zakres jej funkcjonalności został przedstawiony na diagramie przypadków użycia (rys.2).

W naszym systemie istnieją trzy typu użytkowników: Użytkownicy, Pracownicy i Administratorzy. Użytkownicy mają możliwość tylko zobaczenia danych o studentach. Pracownicy jak i Administratorzy po zalogowaniu mogą edytować dane studentów. Najwięcej uprawnień mają Administratorzy, którzy nie tylko mogą zastępować pozostałych aktorów, ale także mogą administrować systemem. Administrowanie systemem polega na dodawaniu nowych użytkowników i ich kasowaniu ich oraz daje możliwość przypisania użytkownikom funkcji.

clip_image004

Rys. 2 Funkcjonalność aplikacji – diagram przypadków użycia

W przedstawionym przykładzie zdefiniowane są dwie role: pierwsza to Pracownik odpowiadające aktorowi „Pracownik” a druga to Admin odpowiadająca aktorowi Administrator. Użytkownicy nie mają przypisanej żadnej roli gdyż obejrzenie pierwszej strony nie wymaga logowania.

Zaprezentowane rozwiązanie jest skonstruowane w oparciu o trójwarstwową architekturę aplikacji webowych wykorzystujących ASP.NET. Poniżej (rys.3), przedstawiono rozmieszczenie klas biorących udział w zabezpieczaniu aplikacji ASP.NET.

clip_image006

Rys.3 Trójwarstwowa struktura rozmieszczenia klas w mechanizmie bezpieczeństwa Forms – diagram klas (Three-Tier Diagram)

Klasy te mają nadane stereotypy w postaci odpowiednich ikon. I tak w warstwie prezentacji znajdują się klasy odpowiedzialne za komunikację użytkownika z systemem. Znajdują się tu klasy specyfikujące strony aspx oraz ascx i mają one stereotyp klas interfejsu (ang. Boundary Class). W warstwie usług znajdują się klasy ze stereotypem klasy sterującej (ang. Control Class), które sterują aplikacją i odpowiadają za jej działanie. Klasy te stanowią most pomiędzy klasami znajdującymi się w pozostałych warstwach. W warstwie danych natomiast znajdują się klasy ze stereotypem klas danych (ang. Entity Class) i reprezentują one tabele w bazie danych.

Korzystając z klas zamieszczonych na rysunku 3 możemy przedstawić scenariusz obrazujący proces logowania.

clip_image008

Rys. 4 Logowanie  do aplikacji ASP.NET – diagram sekwencji

Implementacja zabezpieczeń

Pierwszą czynnością, jaką należy wykonać by uruchomić zabezpieczenia aplikacji w oparciu o mechanizmy Forms jest wpisanie do pliku Web.config następujących linijek kodu:

<authentication mode="Forms">
           <!–     ustawienie nazwy ciasteczka na "cookies",
               ustawienie ochrony na wszystkie strony,
               ustawienie czasu ważności ciasteczka na 60 min ->
           <forms name="cookies" protection="All" timeout="60" />
</authentication>

Gdy zostały uaktywnione zabezpieczenia aplikacji należy zaimplementować klasę, która stworzy nam ciasteczko i będzie czuwać nad ochroną każdej strony. Taką funkcjonalność realizuje zdarzenie Application_AuthenticateRequest znajdujące się w klasie Global. Każde zdarzenie w systemie powoduje zbadanie czy jest autoryzacja. Ma to miejsce w linijce

if (Request.IsAuthenticated == true)

Gdy jest autoryzacja następuje sprawdzenie czy istnieje ciasteczko.

if ((Request.Cookies["studentcookies"] == null)||(Request.Cookies["studentcookies"].Value == ""))

Jeśli ciasteczko istnieje to:

{
// Pobranie roli z ciasteczka
                         FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(Context.Request.Cookies["studentcookies"].Value);
          //konwersja roli z danych na liste uporzadkowaną     
ArrayList userRoles = new ArrayList();
     foreach (String role in ticket.UserData.Split( new char[] {´;´} ))
                         {
                              userRoles.Add(role);
                         }
     roles = (String[]) userRoles.ToArray(typeof(String));
}

jeśli natomiast ciasteczko nie istnieje, jest tworzone, szyfrowane algorytmem MD5 i wysyłane do przeglądarki użytkownika.

String[] roles;
     // Tworzenie cookies jeśli nie jest stworzone jeszcze w tej sesji
     if ((Request.Cookies["studentcookies"] == null) || (Request.Cookies["studentcookies"].Value == ""))
                    {
          // Pobranie ról z tabeli użytkownicy i dodanie do ciasteczka
          UzytkownikCTR user = new UzytkownikCTR();
          roles = user.WezRole(User.Identity.Name);
          // stworzenie string, a w celu utwardzenia ciasteczka
          String roleStr = "";
          foreach (String role in roles)
          {
          roleStr += role;
          roleStr += ";";
          }
     // Tworzenie ciasteczka
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
          1,                        // wersja
          Context.User.Identity.Name,  // nazwa użytkownika
          DateTime.Now,                // czas stworzenia
          DateTime.Now.AddHours(1),    // czas wygaśnięcia
          false,                       // nie utworzenie ciasteczka
          roleStr);                    // funkcja(rola) w systemie);
          // Zaszyfrowanie ciasteczka
          String cookieStr = FormsAuthentication.Encrypt(ticket);
     // Wysłanie ciasteczka do przeglądarki klienta
Response.Cookies["studentcookies"].Value = cookieStr;
Response.Cookies["studentcookies"].Path = "/";
Response.Cookies["studentcookies"].Expires = DateTime.Now.AddMinutes(1);

Kolejnym etapem jest stworzenie formularza, który umożliwi zalogowanie się do systemu.

Formularz taki jest stworzony w postaci pliku Logowanie.ascx i razem z plikiem Studenci.ascx są przypisane do głównej strony  Default.aspx. Formularz logowania jest zaimplementowany w prawy panel i pokazuje się wtedy, gdy istnieje konieczność zalogowania – autoryzacja zapytania kończy się niepowodzeniem.

// Sprawdzenie czy jest autoryzacja
          if (Request.IsAuthenticated == false)     
               {
//Jeśli nie ma autoryzacji wyświetlany jest po prawej stronie formularz do logowania
PrawyPanel.Controls.Add(Page.LoadControl("~/ochrona/Logowanie.ascx"));
PrawyPanel.Visible = true;            
               }              

Formularz Logowanie.ascx składa się z dwóch kontrolek typu TextBox i przycisku LogujBtn. Naciśnięcie przycisku powoduje wywołanie metody, która sprawdza poprawność wpisanych danych

// Obsługa przycisku Loguj
       private void LoginBtn_Click(Object sender, ImageClickEventArgs e) {
// Próba sprawdzenia poprawności danych
       UzytkownikCTR konto = new UzytkownikCTR();
String uzytkownikId = konto.Logowanie(login.Text, StudentOchrona.Szyfr(haslo.Text));// kodowanie hasła
           if ((uzytkownikId != null) && (uzytkownikId != ""))
               {
               // Zapisanie nazwy użytkownika w postaci loginu do ciasteczka znajdującego sie po stronie klienta
               FormsAuthentication.SetAuthCookie(login.Text, RememberCheckbox.Checked)
               // Powrót do strony poczatkowej z przed logowania
               Response.Redirect(Request.ApplicationPath);
           }
           else {
Info.Text = "<" + "br" + ">Logowanie nie powiodło się!" + "<" + "br" + ">";
           }
       }     

Gdy zalogujemy się do systemu, znika nam formularz logowania a na banerze (rys. 5), który jest dołączony do każdej strony, pojawia się w prawym górnym rogu komunikat witający zalogowanego użytkownika oraz przycisk Wyloguj, dzięki któremu możemy się wylogować.

clip_image010

Rys. 5 Baner aplikacji „Ewidencja Studentów”

Naciśnięcie tego przycisku uruchamia stronę Wyloguj.aspx, która zawiera kod programu kasujący informacje zawarte w ciasteczku.

public class Wyloguj : System.Web.UI.Page
     {
       private void Page_Load(object sender, System.EventArgs e) {
           // Wylogowanie z systemu autoryzacji
           FormsAuthentication.SignOut()
           // ustawienie wartości ciasteczka na puste lub nieaktualne
Response.Cookies["studentcookies"].Value = null;
Response.Cookies["studentcookies"].Expires = new System.DateTime(1999, 10, 12);
Response.Cookies["studentcookies"].Path = "/";
           // Powrót do strony początkowej
          Response.Redirect("~/Default.aspx");
       }

Ostatnim elementem interfejsu użytkownika, zajmującego się bezpieczeństwem, jest strona BrakUpawnien.aspx. Zawarty jest w niej komunikat informujący użytkownika, o braku uprawnień do uzyskania dostępu do zasobów oraz link do strony głównej gdzie znajduje się formularz logowania. Strona BrakUprawnien.aspx uruchomiona zostanie wtedy, gdy strona do której się próbuje dostać użytkownik jest zabezpieczona i próba autoryzacji przejdzie niepomyślnie. W przedstawionym przykładzie strony są zabezpieczone przed dostępem w dwojaki sposób. Możliwość edycji danych studentów jest chroniona za pomocą poniższego kodu:

//Sprawdzenie autoryzacji
               string funkcje=" ";
               if (Request.IsAuthenticated == true)     
     {
                    // Pobranie roli użytkownika
     UzytkownikCTR osoba = new UzytkownikCTR();
     SqlDataReader dr = osoba.WezFunkcje(Context.User.Identity.Name);
               dr.Read();     
               funkcje= (String) dr["NazwaFunkcji"];
               dr.Close();
     }
               // sprawdza czy przypisane są funkcje
               if ((funkcje=="Pracownik") || (funkcje == "Admins"))
               {
// Gdy przypisane są role pracownika lub administratora to  wyświetlony
// jest link, który daje możliwość dodania nowego użytkownika
                    NowyStudent.Visible=true;
     }

Dostęp do zasobów administratora jest chroniony w inny sposób:

// Sprawdzenie czy obecnie zalogowany użytkownik ma uprawnienia do otworzenia strony
          if (StudentOchrona.SprawdzenieFunkcji("Admins") == false)
               {
          //Przekierowanie na stronę informujacą o braku uprawnień               Response.Redirect("~/ochrona/BrakUprawnien.aspx");
               }

Administrator

W przedstawionym rozwiązaniu, gdy jesteśmy zalogowani jako administrator mamy wszystkie uprawnienia do edycji danych o studentach oraz możliwość dodania i kasowania użytkowników z możliwością przypisania im ról. Aby dodać lub skasować użytkownika należy po zalogowaniu jako administrator, kliknąć na przycisk Administrator znajdujący się w lewym dolnym rogu banera. Przycisk ten jest dostępny tylko, gdy zalogowany użytkownik ma przypisaną rolę Admins. Gdy wejdziemy w zasoby administratora pokaże się nam prosty formularz pozwalający na zarządzanie użytkownikami:

clip_image012

Rys. 6 Zarządzanie użytkownikami

Po wybraniu użytkownika możemy zmienić hasło lub dodać/zabrać uprawnienia.

Edycja danych

Stosowanie zabezpieczeń Forms daje jeszcze jedną ważną funkcjonalność a mianowicie można zapisać w bazie danych, kto dokonał modyfikacji zawartości komórek. W celu wykorzystania tej zalety należy w tabeli z danymi dodać kolumnę, w której będą przechowywane te dane a następnie dodać parametr w metodzie klasy sterującej odpowiedzialny za przekazanie danych do bazy danych. Poniżej znajduje się wycinek klasy sterującej StudentCTR z fragmentem metody poprawiającej dane studenta:

//Metoda PoprawStudenta jest odpowiedzialna za poprawianie danych studenta
          public void PoprawStudenta (int NrAlbumu,  String Nazwisko, String Imie, String Adres, String Wydzial, String Zmodyfikowal)
          {
               // tworzenie egzeplarza dla połączenia z bazą danych
               // parametryPolaczenia sa pobierane z pliku Web.config
          SqlConnection conn= new SqlConnection(ConfigurationSettings.AppSettings["parametryPolaczenia"]);
               //Definioweanie polecenia z bazą danych
          SqlCommand comm = new SqlCommand("Popraw_Studenta", conn);
               // Zdefiniowanie polecenia jako Stored Procedure
               comm.CommandType = CommandType.StoredProcedure;
// Dodawanie parametrów do Stored Procedure
               SqlParameter parameternrAlbumu = new SqlParameter("@nrAlbumu", SqlDbType.Int, 4);
               parameternrAlbumu.Value = NrAlbumu;
               comm.Parameters.Add(parameternrAlbumu);
               (…)
               SqlParameter parameterzmodyfikowal= new SqlParameter("@zmodyfikowal", SqlDbType.NVarChar, 50);
               parameterzmodyfikowal.Value = Zmodyfikowal;
               comm.Parameters.Add(parameterzmodyfikowal);
               // Otworzenie połączenia z bazą danych
               conn.Open();
               // Wykonanie polecenia "Popraw_Studenta"
               comm.ExecuteNonQuery();
               //Zamknięcie połączenia z bazą danych
               conn.Close();              

          }

Dodatkowo, podczas zapisywania danych, należy odwołać się do nazwy użytkownika zawartej w ciasteczku. Odwołanie to uzyskujemy po przez Context.User.Identity.Name a cała linia odpowiedzialna za poprawienie danych studentów wygląda tak:

// Poprawienie danych w tabeli Student
                   dane.PoprawStudenta( NrAlbumu,NazwiskoPole.Text, ImiePole.Text, AdresPole.Text,WydzialPole.Text,Context.User.Identity.Name );

Podsumowanie

Przedstawione rozwiązanie pokazuje tylko czubek góry lodowej, jaką jest zabezpieczanie aplikacji ASP.NET przy wykorzystaniu mechanizmów bezpieczeństwa Forms. Wykorzystanie ciasteczek daje możliwość personalizacji naszych stron, ciasteczka sprawdzają się w aplikacjach WWW używających ASP.NET. Zabezpieczenia Forms, poprzez uwierzytelnianie umożliwiają zarządzanie dostępem do poszczególnych zasobów systemu różnym typom użytkowników. Wykorzystanie ciasteczek jest proste w implementacji i pozwala na zastosowanie niezależnie od platformy systemu operacyjnego, co daje przewagę zwłaszcza przy zabezpieczaniu aplikacji internetowych.

Kod źródłowy aplikacji i backup bazy danych.

Wykaz literatury

McManus J.P., Kinsman C., „C# Developer´s Guide to ASP.NET, XML, and ADO.NET”, Addison Wesley, 2002

Microsoft Corporation „Tworzenie Bezpiecznych Aplikacji Microsoft ASP.NET”, Microsoft Press, 2002

Technorati Tagi: ASP.NET,aplikacje webowe

Zostaw odpowiedź

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Możesz użyć tych HTML tagów i atrybutów: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Close