Kontrola dostępu w aplikacjach webowych
Transkrypt
Kontrola dostępu w aplikacjach webowych
Bezpieczeństwo RBAC Kontrola dostępu w aplikacjach webowych Tworząc aplikacje webowe, szczególnie te bardziej skomplikowane, realizujące złożoną logikę biznesową, bardzo często spotykamy się z problemem kontroli dostępu. Każdy system, pracujący na danych o kluczowym znaczeniu, powinien mieć możliwość ograniczania dostępu do nich dla różnych użytkowników. Dowiesz się... Powinieneś wiedzieć... • Zaprezentujemy model RBAC i pokażemy, jak przy jego użyciu zaprojektować elastyczny i rozszerzalny moduł kontroli dostępu. • Wskazana jest znajomość architektury MVC i wzorca dekoratora; • Podstawowa wiedza z zakresu kontroli dostępu. Poziom trudności W artykule omówimy pokrótce teoretyczny model kontroli dostępu, opartej o role w konfrontacji z innymi powszechnie stosowanymi rozwiązaniami, a następnie zaproponujemy przykładową implementację kontroli dostępu w aplikacji webowej, stosującej wzorzec kontrolera strony. Problem Przy tworzeniu praktycznie każdej aplikacji, wykorzystywanej przez więcej niż jednego użytkownika, trzeba zmierzyć się z problemami kontroli dostępu. Jeden z nich to uwierzytelnianie, a więc weryfikacja tożsamości podmiotu. Drugi to autoryzacja, czyli sprawdzanie, czy dany podmiot ma dostęp do zasobów, o które prosi. W aplikacjach webowych uwierzytelnianie odbywa się na ogół poprzez podanie loginu i hasła. Implementacja ekranu logowania, to temat opisywany już wielokrotnie, więc na potrzeby tego tekstu założymy, że mechanizm uwierzytelniania jest już gotowy i skupimy się na bardziej złożonym problemie autoryzacji. Powszechną praktyką, stosowaną przez twórców aplikacji webowych (nawet jeśli nie są tego świadomi), jest zdefiniowanie trzech podstawowych poziomów dostępu: • Anonimowy użytkownik to taki, który nie przeszedł procesu uwierzytelnienia. 40 Na ogół ma dostęp jedynie do ekranu logowania (system intranetowy) lub może korzystać z pewnych podstawowych funkcji (system internetowy); • Zalogowany użytkownik to taki, który pomyślnie przeszedł proces uwierzytelnienia. Konta użytkowników są na ogół tworzone przez administratora (system intranetowy) lub ich samych, na przykład po weryfikacji mailowej (system internetowy); • Administrator to specjalny użytkownik, który ma pełną kontrolę nad systemem. Często (choć nie zawsze) korzysta w tym celu z odrębnego panelu administratora. Takie rozwiązanie sprawdza się całkiem nieźle w prostych scenariuszach, gdy wszyscy użytkownicy są równorzędni, a administratorów jest niewielu i mają władzę absolutną. To założenie można śmiało przyjąć w przypadku wielu aplikacji internetowych, może stąd wynika jego powszechność. Jednak rzadko kiedy, tak prosty model okazuje się wystarczający dla intranetowych systemów o strategicznym znaczeniu, modelujących logikę biznesową firmy. W takiej sytuacji oczywiste jest, że szef powinien mieć inny poziom dostępu niż szeregowy pracownik, ten jeszcze inny niż podwykonawca, a dział techniczny inne uprawnienia niż dział obsługi klienta. Rozwiązanie, które często stosowane jest w takich sytuacjach, polega na sztywnym określeniu klas użytkowników, a następnie ogra- niczeniu dostępu do pewnych obszarów czy funkcji jedynie dla wybranych klas, poprzez zapisanie warunków bezpośrednio w kodzie. Na przykład w systemach śledzenia błędów często rozróżniani są zgłaszający (mogący tworzyć nowe zgłoszenia), programiści (mogący dodatkowo modyfikować stan zgłoszeń) i menedżerowie (mogący generować raporty i zestawienia). W przypadku systemu, w którym zadania są jasno zdefiniowane i niezmienne w czasie (jak wspomniany już system śledzenia błędów) takie rozwiązanie może się sprawdzić. Jednak zastosowanie tego podejścia w dużym systemie biznesowym, rozwijanym w warunkach ciągle zmieniających się wymagań, częstych reorganizacji i zmian strategii firmy ma liczne wady. Po pierwsze, cała logika kontroli dostępu zapisywana jest bezpośrednio w kodzie, jej modyfikacja wymaga ingerencji u źródła. Weryfikacja, dbanie o jej spójność i bezpieczeństwo jest mocno utrudnione. Co więcej, audyt wymaga wiedzy programistycznej i znajomości architektury systemu. Logika kontroli dostępu rozrzucona jest w różnych miejscach kodu, co utrudnia tworzenie nowych klas użytkowników czy implementację nowych modułów systemu, zwiększa też prawdopodobieństwo pomyłek. W końcu nie ma możliwości wdrożenia w oparciu o te same źródła kilku instancji systemu, z których każda ma inaczej zdefiniowaną logikę kontroli dostępu. Warto pamiętać także o tym, że każda zmiana w kodzie wymaga testów, co więcej, na serwerze produkcyjnym pojawi się nie od razu, a dopiero po wydaniu nowej wersji aplikacji. Jak widać, w takiej sytuacji cała odpowiedzialność za zarządzanie dostępem spada na programistów, podczas gdy powinna spoczy������� ���� ���� ���� ���� ���� ����������� Rysunek 1. Model RBAC 04/2007 RBAC wać na administratorze systemu, czy osobie odpowiedzialnej za bezpieczeństwo informacji w firmie. Takie rozwiązanie, choć często stosowane, narzuca wiele ograniczeń, które mogą okazać się nie do przyjęcia. W dalszej części artykułu zaproponujemy alternatywne rozwiązanie, dzięki któremu można uniknąć większości powyższych problemów. Opiszemy prosty, a zarazem bardzo elastyczny model RBAC i zaprezentujemy jego przykładową implementację. Model RBAC Kontrola dostępu oparta o role (ang. Role-Based Access Control, w skrócie RBAC), to teoretyczny model kontroli dostępu, od lat powszechnie stosowany w aplikacjach biznesowych, będący alternatywą dla tradycyjnych modeli MAC i DAC. Co ciekawe, formalizacji doczekał się dopiero w publikacji z roku 1992 (odnośnik do niej dostępny jest w materiałach do artykułu), gdy był już szeroko stosowany w praktyce. Podstawowym pojęciem w RBAC jest, jak łatwo się domyślić, rola, opisująca pewną funkcję czy stanowisko w organizacji. Kolejnym ważnym obiektem są uprawnienia, reprezentujące operacje udostępniane przez system. Uprawnienia przypisywane są do ról, określając tym samym ich prawa. Z kolei podmiotem określa się każdego użytkownika aplikacji, a także zewnętrzny system korzystający z jej usług. Podmiot może posiadać wiele ról, odpowiadających jego pozycji w strukturze organizacji. Zależności pomiędzy poszczególnymi pojęciami modelu RBAC ilustruje Rysunek 1. Niezwykle istotną własnością modelu RBAC jest to, że podmiot otrzymuje uprawnienia wyłącznie poprzez role, które posiada. Innymi słowy, z punktu widzenia kontroli dostępu, istotne jest jedynie stanowisko użytkownika, a nie jego tożsamość. Ta prosta własność sprawia, że struktura uprawnień staje się dużo bardziej przejrzysta i łatwiejsza w administracji. Warto zaznaczyć, że uprawnienia powinny opisywać operacje na poziomie logiki biznesowej, a nie prawa zapisu lub odczytu, do niskopoziomowych zasobów (jak pliki czy tabele w bazie danych). Innymi słowy zarówno role, jak i uprawnienia powinny być definiowane w języku konkretnej organizacji, na podstawie wcześniejszej analizy. Na koniec warto wspomnieć o pewnej bardzo dobrej praktyce kontroli dostępu, dotyczącej nie tylko modelu RBAC. Nazywana jest zasadą najmniejszych uprawnień (ang. Principle of Least Privilege) i mówi o tym, że użytkownik nie powinien posiadać większych uprawnień, niż to wymagane do wykonywania swoich obowiązków. Warto pamiętać o tym przy administracji uprawnieniami i rolami. www.phpsolmag.org Przykład implementacji Omówiliśmy pokrótce podstawy teoretyczne, spróbujmy więc zaimplementować RBAC w przykładowej aplikacji webowej. Wyjdziemy od analizy przypadków użycia, która ułatwi określenie potrzebnych ról i uprawnień. Następnie, korzystając ze wzorca dekoratora (opisanego m.in. w artykule „Dekorator: wzorzec projektowy na każdą bolączkę” z numeru 4/2006, dostępnego do ściągnięcia na stronie PHP Solutions), zaprojektujemy i zaimplementujemy klasy, odpowiadające za kontrolę dostępu w przykładowej architekturze stosującej kontroler strony. Skupimy się na najbardziej interesujących z punktu widzenia niniejszego artykułu aspektach, nie zajmując się chociażby spo- sobem dostępu do bazy danych czy przebiegiem procesu logowania. Przykład celowo nie jest związany z żadnym konkretnym frameworkiem (choć opiera się na architekturze stosowanej w wielu z nich), aby pokazać ogólność rozwiązania. Przypadki użycia Rozważmy typowy system obsługi zgłoszeń (ang. trouble tickets), którego głównym zadaniem jest usprawnienie kontaktu z klientami. Po analizie dziedziny mógłby powstać diagram przypadków użycia taki, jak na Rysunku 2. (mocno uproszczony dla celów przykładu). Pomoże on nam zdefiniować role i uprawnienia. Jak wiadomo, role opisują funkcje, jakie w organizacji pełnią użytkownicy (mówiąc bar- ��������������������� ������������������ ������ ������������������ ���������� ��������������������� ������������������� �������� Rysunek 2. Diagram przypadków użycia ������������� ���������� ������������ ����������������������� ������������������ �������������� ������������ ����������������������� ����������������� ����������������� ��������������� ���������������� ������������������ ������������������ ����������������� ������������������� ������������������ Rysunek 3. Diagram kontrolerów 41 Bezpieczeństwo dziej ogólnie, podmioty). Jeśli korzystamy z diagramu przypadków użycia, dobrymi kandydatami na role są aktorzy (choć nie zawsze musi to być relacja jeden-na-jeden). W opisanym przykładzie możemy wyróżnić następujące role: • Klient: osoba korzystająca z systemu, aby zgłosić swój problem i śledzić jego stan; • Konsultant: pracownik odpowiadający za obsługę zgłoszeń klientów; • Menedżer: osoba nadzorująca pracę konsultantów. Kolejnym krokiem jest określenie uprawnień, najmniejszego i niepodzielnego kwantu, umożliwiającego kontrolę dostępu w modelu RBAC. Role zawsze można zmienić, jednak odpowiedni dobór ,,rozdzielczości” uprawnień, to ważna decyzja projektowa. Od tego zależy, jak łatwe i elastyczne będzie definiowanie ról, i jak precyzyjna kontrola dostępu będzie możliwa. Także w tym przypadku można oprzeć się na przypadkach użycia, często jednak okazu- ������������� je się, że uprawnienia powinny być nieco bardziej szczegółowe i niskopoziomowe. Wtedy jeden przypadek użycia można rozbić na kilka uprawnień. Implementacja kontroli dostępu staje się znacznie łatwiejsza, jeśli uprawnienia mają swoje odpowiedniki w samym systemie. Dlatego w przykładzie opisanym poniżej zastosujemy kontrolę uprawnień w warstwie kontrolera, a uprawnienia utożsamimy ze zdarzeniami kontrolera (a same kontrolery zaprojektujemy tak, aby ich zdarzenia odpowiadały uprawnieniom). Kontrolery Projektując architekturę aplikacji webowej, najprawdopodobniej skorzystamy z jakiegoś rodzaju kontrolera, obsługującego żądania HTTP. Jeśli wszystkie żądania przechodzą przez kontroler, wybór uprawnień i implementacja kontroli dostępu staje się prosta i przejrzysta. Załóżmy, że stosujemy wzorzec Page Controler (choć podobny sposób rozumowania można także zastosować dla Front Control- lera), a każdy kontroler to osobny plik PHP, obsługujący żądanie dla danego URL. Dodatkowo zakładamy, że kontroler otrzymuje jako parametr żądania nazwę zdarzenia, które ma obsłużyć. Wszystkie kontrolery dziedziczą po wspólnej abstrakcyjnej nadklasie i implementują metody onAction(), gdzie action to nazwa zdarzenia, które obsługuje dana metoda. W uproszczonym przykładzie projektowanego systemu obsługi zgłoszeń, podstawowymi pojęciami są zgłoszenia i raporty, a więc to dla nich utworzymy kontrolery. Projektując zdarzenia najlepiej wyjść od przypadków użycia, choć często okazuje się, że zdarzenia powinny być bardziej szczegółowe. Dobrym przykładem są tu tzw. przypadki użycia CRUD (ang. Create, Read, Update, Delete), łączące operacje tworzenia obiektu, jego modyfikacji i usuwania (a więc trzech osobnych zdarzeń) w jeden przypadek użycia ,,zarządzanie obiektem”. Przykładowy diagram kontrolerów dla naszego systemu obsługi zgłoszeń znajduje się na Rysunku 3. ������������������� ������������ ����������������������� ������������������ ���������� ������������ ����������������������� ������������������ �������������� ����������������������� ������������ ����������������������� ������������ ������� ����������������������������������������������������������� ������������������� Rysunek 4. Diagram dekoratorów kontrolera ����� ������������������������ ��������������� �������� ��� ��� ������������������������������ ������������������������������ ���������������������� ������������ ������������� ��� Rysunek 5. Diagram sekwencji kontroli dostępu 42 04/2007 Bezpieczeństwo Jak łatwo się już domyślić, w aplikacji webowej o takiej architekturze, dobrym kandydatem na uprawnienie (w rozumieniu modelu RBAC) będzie para $(kontroler, zdarzenie)$. Przemawia za tym kilka faktów: • Kontrolę uprawnień możemy przeprowadzić na poziomie kontrolera, wtedy staje się ona przezroczysta dla programisty, co ułatwia pracę, ale także ogranicza możliwe dziury w bezpieczeństwie wynikające z przeoczeń; • Taki wybór uprawnień sprawia, że mechanizm kontroli dostępu staje się rozszerzalny wraz z rozbudową systemu, a narzut związany z jego utrzymaniem i aktualizacją jest minimalny (nowe uprawnienia mogą być nawet rozpoznawane Listing 1. Dekorator kontrolera { Tabela 1. zawiera podsumowanie przypadków użycia, odpowiadających im zdarzeń i uprawnień w projektowanym przez nas systemie. W dalszej części artykułu zaproponujemy implementację przezroczystej dla kontrolera kontroli dostępu, przy założeniu, że uprawnieniami są zdarzenia kontrolera. } Dekorowanie kontrolera abstract class ControllerDecorator implements Controller { protected $decoratedController; function __construct(Controller $decoratedController) $this->decoratedController = $decoratedController; function getEventName() { return $this->decoratedController->getEventName(); } function getName() { return $this->decoratedController->getName(); } function run() { return $this->decoratedController->run(); } } Listing 2. Dekorator kontroli dostępu class ControllerRbacDecorator extends ControllerDecorator { private $profile; public function __construct(Controller $decoratedController, Profile $profile) { $this->profile = $profile; parent::__construct($decoratedController); } function run() { if (!$this->profile->hasPermission($this->getName(), $this->getEventName())) { throw new Exception("Brak uprawnień"); } return $this->decoratedController->run(); } } 44 automatycznie, za pomocą refleksji klas kontrolerów); • Równoważność uprawnień i zdarzeń kontrolera ułatwia komunikację w zespole, nie ma potrzeby tworzenia i dokumentowania nowego pojęcia. Sprawę dodatkowo ułatwia fakt, że oba pochodzą od przypadków użycia, a te bezpośrednio powiązane są z aktorami (a więc i rolami). Skoro uprawnienia są jednoznaczne z prawami dostępu do zdarzeń kontrolera, a każde żądanie musi przejść przez kontroler, dobrym miejscem na implementację kontroli dostępu jest właśnie klasa kontrolera. Oczywiście można zapisać odpowiednie warunki w kodzie nadklasy, po której dziedziczą kontrolery (albo utworzyć klasę po niej dziedziczącą), jednak nie jest to zbyt eleganckie rozwiązanie. Obsługa żądań i kontrola dostępu to dwa różne zadania, które nie powinny być łączone. W takich sytuacjach dobrym pomysłem jest skorzystanie z wzorca dekoratora, który pozwala na dynamiczne zwiększanie zakresu odpowiedzialności obiektu. Rysunek 4. zawiera zmodyfikowany schemat klas kontrolerów, rozbudowany o abstrakcyjny dekorator i dziedziczący po nim dekorator przeprowadzający kontrolę uprawnień. Na Listingu 1. znajduje się kod dekoratora, realizującego interfejs kontrolera, który w konstruktorze otrzymuje obiekt do udekorowania i przekierowuje do niego wszystkie metody interfejsu. Warto zauważyć, że klasa jest zdefiniowana jako abstrakcyjna mimo, że nie posiada żadnych abstrakcyjnych metod. Po prostu tworzenie obiektów tej klasy nie ma żadnego sensu, istnieje tylko po to, aby konkretne dekoratory dziedziczące po niej, nie musiały za każdym razem implementować mechanizmu dekoracji. Na Listingu 2. znajduje się kod dekoratora przeprowadzającego kontrolę dostępu przed przekazaniem sterowania do metody realizującej zdarzenie. Zakładamy, że w konstruktorze został przekazany profil opisujący aktualnie zalogowanego użytkownika. Metoda hasPermission() profilu sprawdza (przeglądając wszystkie role), czy użytkownik ma uprawnienia do danego kontrolera i zdarzenia. Jeśli nie, dekorator wyrzuca wyjątek i nie 04/2007 RBAC Tabela 1. Związek przypadków użycia, metod kontrolera i uprawnień W Sieci • • David Ferraiolo, Richard Kuhn. Role-Based Access Control, http://csrc.nist.gov/rbac/ Role_Based_Access_Control-1992.html; Wikipedia: Role-Based Access Control, http://en.wikipedia.org/wiki/Role-Based_Access_Control. przekazuje sterowania do dekorowanego kontrolera. Utworzenie i wywołanie udekorowanego kontrolera przebiega w następujący sposób (zakładając, że mamy referencję do profilu użytkownika): Przypadek użycia Metoda kontrolera Uprawnienie Utworzenie zgłoszenia TicketsController::onCreate() (tickets, create) Podgląd zgłoszenia TicketsController::onView() (tickets, view) Zamknięcie zgłoszenia TicketsController::onClose() (tickets, close) Dodanie komentarza TicketsController::onComment() (tickets, comment) Generowanie raportu ReportsController::onGenerate() (reports, generate) } catch (Exception $e) { wzorca dekoratora, udało się w prosty sposób zaimplementować przezroczysty dla programisty i rozszerzalny moduł kontroli dostępu. Dzięki zastosowaniu modelu RBAC logika kontroli dostępu nie jest zapisana bezpośrednio w kodzie, a w dynamicznych zależnościach między podmiotami, rolami i uprawnieniami. Dzięki temu cała struktura jest bardzo przejrzysta, łatwa w administracji i może być modyfikowana przez osoby nie będące programistami. Co więcej, polityka bezpieczeństwa może różnić się między kilkoma instalacjami systemu. } Możliwe ulepszenia $controller = new ControllerRbacDe corator(new TicketsController(), try { $profile); $controller->run(); echo "Błąd: ". $e->getMessage(); Nie są wymagane żadne zmiany w samych klasach kontrolera, za kontrolę dostępu odpowiada tylko dekorator. Jak to działa Na koniec warto przeanalizować diagram sekwencji przedstawiony na Rysunku 5., przedstawiający dynamiczny obraz procesu kontroli dostępu. Przeglądarka użytkownika wysyła żądanie HTTP, które serwer kieruje do odpowiedniego kontrolera, wywoływana jest jego metoda run(). Kontroler został udekorowany, więc dekorator najpierw pobiera nazwę dekorowanego kontrolera oraz zdarzenia, które zostało wywołane przez użytkownika. Następnie profil proszony jest o sprawdzenie, czy aktualnie zalogowany użytkownik ma uprawnienia do danego kontrolera i zdarzenia (i czy jest w ogóle zalogowany). To, jak działa i kiedy tworzony jest profil, nie wchodzi w zakres niniejszego artykułu. Warto jednak wspomnieć, że profil powinien obsługiwać logowanie, wylogowanie i przechowywanie informacji o użytkowniku w sesji, a także powinien przekierowywać niezalogowanego użytkownika do kontrolera logowania (który oczywiście nie może być dekorowany kontrolą dostępu). Profil, sprawdzając uprawnienia, powinien opierać się na liście ról bieżącego użytkownika. Jeśli posiada on dane uprawnienie, kontrola przekazywana jest do udekorowanego kontrolera, który z kolei wywołuje metodę obsługującą dane zdarzenie. Jeśli nie, wyrzucany jest wyjątek, który oczywiście powinien zostać odpowiednio obsłużony. Jak widać dzięki założeniu, że wszystkie żądania muszą przejść przez kontroler i dzięki użyciu www.phpsolmag.org Zaprezentowany powyżej szkic implementacji nie jest jedynym możliwym, można go rozwinąć i ulepszyć na wiele sposobów, oto kilka z nich: • Przydatny będzie panel administracyjny, pozwalający na zarządzanie rolami, przypisywanie uprawnień do ról i ról do użytkowników. Oczywiście dostęp do takiego modułu także można, i należy chronić za pomocą opisanego mechanizmu kontroli dostępu; • Główną wadą kontroli dostępu na poziomie kontrolera jest to, że jest ściśle związana z żądaniami HTTP i warstwą kontrolera. Jeśli system posiada bogaty model domeny, wykorzystywany przez więcej niż jedną aplikację, warto rozważyć kontrolę dostępu w warstwie domeny. W takiej sytuacji teoretyczne podstawy pozostają bez zmian, ale zmienia się znaczenie uprawnień i implementacja kontroli dostępu; • Można zmodyfikować szablony tak, aby deaktywować czy nawet usuwać odnośniki do operacji, do których bieżący użytkownik nie ma dostępu; • Łatwo napisać skrypt, który automatycznie zaktualizuje tabelę uprawnień na podstawie analizy kodu kontrolerów. Dzięki temu nie trzeba pamiętać o uzupełnianiu tabeli uprawnień w bazie wraz z rozwojem systemu. Taki skrypt może po prostu szukać w plikach źródłowych wzorców, odpowiadających metodom kontrolera (np. przy użyciu wyrażeń regularnych), albo wykorzystać mechanizm refleksji. Na podstawie zebranych danych można synchronizować tabelę uprawnień. W bardziej skomplikowanych projektach można rozważyć zastosowanie bardziej zaawansowanych aspektów modelu RBAC: • Nietrudno dodać, bardzo przydatny w praktyce, mechanizm dziedziczenia uprawnień. Dzięki temu menedżer będzie mógł dziedziczyć z roli konsultanta, przejmując wszystkie uprawnienia roli dziedziczonej i uzupełniając je o uprawnienia specyficzne dla roli dziedziczącej (podobnie do mechanizmu dziedziczenia w programowaniu obiektowym); • Założyliśmy, że użytkownik po zalogowaniu może korzystać ze wszystkich swoich ról i związanych z nimi uprawnień. Alternatywnym i bardziej zgodnym z modelem RBAC rozwiązaniem jest wymuszenie wyboru jednej z dostępnych ról w momencie logowania. Wtedy powstaje sesja, łącząca użytkownika z aktywną rolą (nie mylić z mechanizmem sesji PHP); • Można zaimplementować politykę separacji obowiązków (ang. Separation of Duties), mającą na celu ograniczenie zagrożeń wynikających z łączenia kilku ról przez jednego pracownika. Ta zasada mówi, że dla pewnego zbioru operacji (np. zlecenia przelewu i akceptacji przelewu) nie może istnieć osoba, która ma uprawnienia do wszystkich z nich. Zachęcamy Czytelników do dalszych eksperymentów i dzielenia się swoimi pomysłami. Podsumowanie Stosując sprawdzony model kontroli dostępu opartej o role, udało nam się w łatwy sposób zaprojektować prosty, elegancki i elastyczny mechanizm kontroli dostępu. Uniknęliśmy zaszywania kontroli dostępu bezpośrednio w kodzie, pozwalając na dynamiczne przydzielanie uprawnień do ról, a zarazem ułatwiając administrację uprawnieniami. ADAM BYRTEK Autor odpowiada za zespół programistyczny, zajmujący się rozwojem wewnętrznego systemu OSS/BSS dla ogólnopolskiego dostawcy internetu. Kontakt z autorem: [email protected] http://adambyrtek.blogspot.com/ 45