Podstawy bezpieczeństwa stron internetowych

W dzisiejszych czasach, każdy twórca stron z podstawową wiedzą o PHP i MySQL może pisać rozmaite elementy zapewniające interakcję z użytkownikami, czy oddzielające kod od treści strony. Typowym przykładem jest system komentarzy lub panel administracyjny. Przy wystarczająco dużej determinacji ktoś mógłby zaraz po zapoznaniu się z PHP i MySQL, oraz posługując się manualami, napisać własne forum dyskusyjne. I tutaj pojawia się ciekawostka - w sieci jest wiele kursów PHP, lepszych i gorszych, jednak znalezienie takiego, który by jednocześnie informował o podstawach w kwestii bezpieczeństwa graniczy z cudem - stąd pomysł na ten artykuł.
Od razu na początku chciałbym postawić sprawę jasno - bezpieczeństwo to temat-rzeka, który tutaj ledwo dotkniemy. Sam dysponuję raczej podstawową wiedzą w tych sprawach. Artykuł ma na celu uświadomienie o nieoczywistych dla początkujących zagrożeniach czyhających na strony internetowe, oraz przedstawienie sposobów na walkę z najprostszymi z nich. Innymi słowy - nie daję żadnej gwarancji, że po zastosowaniu się do zawartych tutaj rad nikt nie rozwali twojej strony, a jedynie, że nie zrobi tego byle dzieciak w 1 minutę. Gdy mistrz w tej grze postawi sobie za punkt honoru zepsucie twojej strony, to i tak najprawdopodobniej mu się to uda, niezależnie od twoich starań.

SQL injection (wstrzyknięcie SQL-a)


Zaczniemy od tej metody, gdyż moim zdaniem jest najciekawsza. Przyjrzyjmy się następującemu skyptowi logowania:
<?php

$mysql=mysql_connect("localhost", "***", "***");	
mysql_select_db(baza);
$wynik=mysql_query("SELECT * FROM uzytkownicy WHERE 
login='".$_POST['login']."' AND password='".$_POST['password']."'");
$dane=mysql_fetch_assoc($wynik);

if($dane[id])
   echo "Udany login!";
else
   echo "Błędny login!";
 
mysql_close($mysql);

?>
Oczywiście jest to wręcz obrzydliwie uproszczony skrypt logowania - nie koduje nawet hasła w sha1, czy md5, ale służy on tylko do zilustrowania działania tej metody (kogoś, kto wstawiłby ten skrypt na swoją stronę bez wahania nazwałbym szaleńcem). Przyjmijmy, że w systemie jest tylko jeden użytkownik o loginie "abc" i haśle "abc". Gdy podamy te dane, to otrzymamy oczywiście informację "Udany login!". Gdy podamy jakiekolwiek inne, otrzymamy "Błędny login!"... zaraz, jesteś tego pewien? Podaj dowolny login (np. "x") i hasło: x' OR '1'='1
Co otrzymasz? Otrzymasz nic innego jak "Udany login!". Jeśli nie wierzysz to sam sprawdź, ja natomiast krótko wyjaśnię w czym rzecz. Otóż przy takich danych skrypt tworzy następujące zapytanie MySQL:
SELECT * FROM uzytkownicy WHERE login='x' AND password='x' OR '1'='1'
Tym sposobem zapytanie jest prawdziwe dla wszystkich danych w tabeli, więc wszystkie są wyciągane. W dalszej części skryptu instrukcja warunkowa sprawdza, czy został wyciągnięty jakikolwiek rekord z bazy. Ponieważ został (i to nawet nie jeden, jeśli mamy więcej użytkowników), wynikiem jest udane zalogowanie. Wyobraź sobie teraz, że na taki atak jest wrażliwy twój panel administracyjny, szkody mogłyby być katastrofalne. Ale to jeszcze nic. Ponieważ jeśli dodatkowo na twoim serwerze MySQL jest kiepsko skonfigurowany, to używając w haśle średnika (dla oddzielania kolejnych poleceń), można zrobić użytek z takich poleceń jak DROP TABLE, czy DROP DATABASE... Do tej pory pewnie myślałeś, że ten tekst we wstępie o dzieciaku rozwalającym ci w minutę stronę był żartem. Cóż - nie był.
Naturalnie podany wyżej przykładowy skrypt został celowo napisany tak, aby był wrażliwy. W dość prosty sposób zmieniając postać zapytania MySQL możemy uodpornić skrypt:
$wynik=mysql_query("SELECT * FROM uzytkownicy WHERE login='".mysql_real_escape_string($_POST['login'])."' AND password='".mysql_real_escape_string($_POST['password'])."'");
Funkcja mysql_real_escape_string() informuje PHP, że ma traktować apostrofy jako zwykłe znaki. Tym samym, podając dane nie możemy już podawać instrukcji MySQL-owi. Aby się zabezpieczyć przed SQL injection musisz bezwzględnie używać funkcji mysql_real_escape_string(), dla każdej zmiennej podawanej do zapytania przez użytkownika. Swoim własnym danym na ogół możesz zaufać, jednak eksperci doradzają zabezpieczanie się również przed nimi (w razie ich zmiany poprzez inny rodzaj ataku). Najbezpieczniej jest więc stosować tę funkcję zawsze.
Uwaga: Podany tutaj przykład skryptu logującego, nawet po uwzględnieniu poprawki zapobiegającej użyciu SQL injection jest mało bezpieczny, choćby dlatego, że hasła będące w bazie nie są w żaden sposób zakodowane.

XSS (Cross-Site Scripting)


Na tę metodę szczególnie wrażliwe są m.in. systemy komentarzy. Jeśli pozwalasz swoim użytkownikom na używanie (X)HTML w ich komentarzach, to jest to pole dla popisów dla XSS. Najczęściej wykorzystywany jest tu JavaScript z jakimś złośliwym kodem, często wykradającym od użytkownika jakieś dane, np. cookies. Ten typ ataku wykorzystuje zaufanie, jakie użytkownik ma do twojego serwisu.
Najbardziej skuteczną, a zarazem najprostszą metodą obrony przed XSS jest uniemożliwienie użytkownikom stosowania (X)HTML, np. używając funkcji htmlentities(), która zapobiegnie potraktowaniu podanych tagów jako części kodu strony. Tak samo, jak w przypadku SQL injection, warto uważać również przy wyświetlaniu danych znajdujących się już w bazie.
Często jednak chcemy, aby użytkownicy mieli możliwość używania niektórych tagów (jak np. <b></b>), wtedy już musimy się pobawić w parsowanie treści komentarza użytkownika. Idealnie nadają się do tego celu wyrażenia regularne (można ich używać np. w funkcji preg_replace()), wymaga to już jednak trochę pracy.

CSRF (Cross Site Request Forgeries)


Ten typ ataku wykorzystuje możliwość używania na stronach elementów z zewnętrznych serwerów. W komentarzu można np. umieścić w tagu img niezwykły "obrazek" - tj. nie w formacie jpg, czy png, ale php. "Obrazek" zawierający złośliwy kod. A ponieważ można takiemu "obrazkowi" przekazać też zmienne poprzez GET, to konsekwencje mogą być straszne. Dla przykładu atakujący może umieścić w ten sposób odwołanie do skryptu z twojego panelu administracyjnego (razem z odpowiednimi zmiennymi GET) usuwającymi jakiś element strony z bazy. Gdy na stronę wejdzie zalogowany administrator, kod się wykona - atakujący osiągnie swój cel i to bez żadnego wykradania haseł.
Walka z tą metodą jest podobna, jak w przypadku XSS - musisz bardzo uważać co pozwalasz wyświetlać na swojej stronie użytkownikom. Najlepiej pozwalaj im jedynie na tagi służące do podstawowego formatowania tekstu i nic więcej.

Podsumowanie


Ochronę strony przed tymi atakami można streścić w paru punktach:
  • nie ufaj użytkownikom! - zawsze kontroluj dane wprowadzane przez odwiedzających twoją stronę
  • uważaj nie tylko przy wprowadzaniu danych do bazy, ale również przy ich wyświetlaniu
  • rób kopie bezpieczeństwa - nie tylko plików strony, ale i bazy; najlepsze techniki ochronne mogą zawieść, a bez kopii zostajesz z niczym
  • poprawnie skonfiguruj serwer (oczywiście o ile jesteś jego administratorem, w przeciwnym wypadku musisz mieć nadzieję, że zrobiono to już za ciebie)
Na koniec przypominam informację ze wstępu artykułu - to tylko podstawy. Ten artykuł pomija zupełnym milczeniem inne typy ataków, np. wykradanie sesji, przed którym obrona nie jest już tak prosta (ale nie jest też tak łatwo zastosować ten atak). Liczę jednak na to, że udało mi się przynajmniej uświadomić tobie zagrożenie.
Jeśli chcesz się dowiedzieć więcej o mechanizmie działania XSS i CSRF, to odwiedź: http://www.gajdaw.pl/varia/xss.html

Niniejszy artykuł jest dostępny na licencji Creative Commons Uznanie autorstwa - Użycie niekomercyjne - Na tych samych warunkach 2.5 Polska.


<< Powrót

Komentarze:

Wszystkie przedstawione tutaj opinie należą do ich autorów i twórca strony nie ponosi żadnej odpowiedzialności za ich treść.


Imię/Ksywka (wymagane):

Strona WWW:

Wpisz tekst z obrazka (wymagane):

kod

Treść (wymagane):


W polu "Strona WWW" wpisywanie członu "http://" nie jest konieczne. Tagi (X)HTML wpisane w treści nie będą działać jako element strony, zamiast tego pojawią się w samym komentarzu. Komentarze obraźliwe, nie na temat lub niezgodne z prawem będą w miarę możliwości usuwane.

skiny: grey-tea green | light sky blue | carrot orange
some rights reserved | kanał informacyjny | admin | valid XHTML 1.0 | valid CSS | valid Atom 1.0