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.