bezpieczny Internet


Ochrona danych
..... Poradnik bezpiecznego korzystania z Internetu .....

Zagrożenie atakiem SQL Injection

Ten poradnik dedykujemy webmasterom w celu skutecznego zabezpieczenia własnej strony przed atakiem SQL Injection. Podatność strony na ataki tą metodą są tylko możliwe z powodu błędów projektanta strony. Aby chronić swój serwis przed ingerencją hakera trzeba rozumieć sposób jego działania.

Słabym punktem serwisu jest zawsze formularz lub okienka logowania do których użytkownik ma wprowadzać jakieś dane. W zamierzeniu autora skryptu powinny to być tylko litery lub cyfry, które stanowią dane uwierzytelniające użytkownika lub inne bezpieczne zapytania do bazy danych. Problem zaczyna się wtedy gdy użytkownik celowo lub przypadkowo wprowadzi inne znaki specjalne, które nie są interpretowane jak zwykły tekst tylko jako znaczniki lub operatory. Takie ciągi znaków bez żadnej walidacji są przesyłane jako zapytania do bazy danych, w efekcie może nastąpić przypadkowe uszkodzenie danych lub wykonanie polecenia do którego użytkownik nie był uprawniony.

W prawidłowym zapytaniu SQL stringi powinny być zamknięte pomiędzy znakami apostrofu. W ten sposób interpreter odróżnia przypadkowy tekst od poleceń. Jeśli jednak użytkownik w formularzu wpisze znak apostrofu i wprowadzony tekst nie będzie podlegał walidacji, to w miejscu tego znaku nastąpi zamkniecie oczekiwanego stringu, a wszystko za nim traktowane jest jako kod. Taka luka w zabezpieczeniach może spowodować niegroźny błąd lub skasowanie ważnych danych. Formularze bez walidacji wykorzystywane są przez hakerów do nieautoryzowanego dostępu, kradzieży danych i zawieszania serwera.

Przykład serwisu podatnego na atak SQL to kod prostego formularza z okienkiem do logowania.

<form action="login.php" method="post">
Wpisz login: <input type="text" name="login"> <br>
Wpisz hasło: <input type="password" name="pass"> <br>
<input type="submit" value="zaloguj się"> <br>
</form>

Po wysłaniu takiego formularza serwer wykonuje następujące zapytanie sql:

SELECT USER_NR FROM USERS WHERE (LOGIN='$login') AND (PASSWORD='$pass')

Do zmiennych $login i $pass przypisane są login i hasło wprowadzone w formularzu, jeśli loginem było admin, a hasłem 123#abc! To serwer wykonuje następujące zapytanie SQL:

SELECT USER_NR FROM USERS WHERE (LOGIN='admin') AND (PASSWORD='123#abc!')

Gdy sprawdzi, że wprowadzone dane są zgodne z zapisanymi w bazie to zezwoli na logowanie. Co się jednak stanie, gdy haker nie zna hasła i zamiast niego wprowadzi taki kod:

' OR '1'='1

Nieoczekiwany znak apostrofu na początku, zamyka wcześniej string, czyli jest on pusty a dalej operator OR sprawdza kolejny warunek, czyli czy 1=1, skoro choć jeden z warunków jest spełniony to zapytanie jest prawdziwe I haker uzyskał uwierzytelnienie. Wynikowy kod zapytania wygląda następująco:

SELECT USER_NR FROM USERS WHERE (LOGIN='admin') AND (PASSWORD='' OR '1'='1')

Włamywacz znający tylko login może tak zmodyfikować zapytanie, że tylko pierwszy warunek logowania jest sprawdzany, a hasło może być dowolne, np. nic. Uzyskuje się to przez wpisanie dwóch myślników. W języku SQL "--" oznacza początek komentarza i wszystko co po nim wpisane jest ignorowane. Wystarczy wprowadzić zamiast loginu, następujący ciąg znaków:

admin') --

Zapytanie SQL nie sprawdza drugiego warunku WHERE tylko wszystko po znakach "--" ignoruje jako komentarz. Zapytanie SQL przybiera następujący wygląd:

SELECT USER_NR FROM USERS WHERE (LOGIN='admin') --') AND (PASS='nic')

Pomimo, że "nic" to nie jest prawidłowe hasło, to haker uzyskał dostęp do logowania.

Nawet jeśli dane z okienka logowania są walidowane, to haker może wyrządzać szkody z pośrednictwem innych pól formularza. Na przykład może skasować zawartość tabeli USERS:

') DELETE FROM USERS --

lub nawet skasować całą bazę danych:

') DROP DATABASE PORTAL --

Znając strukturę bazy danych, włamywać może uzyskać dostęp do danych innych klientów. Załóżmy, że ma już konto w danym sklepie, jest zalogowany, a formularz umożliwia mu wyświetlenie własnych zakupów, zapytanie SQL wygląda następująco:

SELECT ARTYKUL_ID, NAZWA, OPIS, CENA
FROM PRODUKTY, ZAKUP
WHERE (PRODUKTY.ARTYKUL_ID=ZAKUP.ARTYKUL_ID)
AND (KLIENT_ID=$id)
ORDER BY NAZWA

Od razu widać, że wystarczy zmienić KLIENT_ID aby podejrzeć zakupy innego klienta. Można też spowodować wyświetlenie zakupów wszystkich klientów wpisując zamiast swojego identyfikatora następujący kod:

0 OR 1=1

W zapytaniu zmieni się tylko ostatni warunek:

SELECT ARTYKUL_ID, NAZWA, OPIS, CENA
FROM PRODUKTY, ZAKUP
WHERE (PRODUKTY.ARTYKUL_ID=ZAKUP.ARTYKUL_ID)
AND (KLIENT_ID=0 OR 1=1)
ORDER BY NAZWA

Skoro ostatni warunek zawsze jest spełniony to wynikiem zapytania jest pojawienie sie historii zakupów wszystkich klientów sklepu.

Włamywacz może pisać bardzo rozbudowane zapytania i wykorzystywać instrukcje UNION.

-1) UNION SELECT KLIENT_ID, LOGIN, HASLO, 0 FROM KLIENCI --

Taka linijka dodana do zapytania SQL tak je modyfikuje, że zwraca w wyniku listę wszystkich loginów, haseł użytkowników sklepu.

Całe zapytanie wygląda następująco:

SELECT ARTYKUL_ID, NAZWA, OPIS, CENA
FROM PRODUKTY, ZAKUP
WHERE (PRODUKTY.ARTYKUL_ID=ZAKUP.ARTYKUL_ID)
AND (KLIENT_ID=-1) UNION SELECT KLIENT_ID, LOGIN, PASSWORD, 0 FROM KLIENCI --)
ORDER BY NAZWA

To tylko najprostsze przykłady błędów popełnianych przez webmasterów, które dają hakerom możliwości ataku. Nawet częściowo walidowane formularze mogą być podatne na ataki. Projektując formularz trzeba z góry przewidzieć, że użytkownik może go testować na wszystkie sposoby, aż znajdzie lukę podatną na atak.

Zabezpieczenie strony przed atakiem SQL Injection

Podstawą bezpieczeństwa jest ograniczone zaufanie do wszystkich wprowadzanych danych. Walidacja polega na sprawdzaniu wprowadzanych stringów czy nie zawierają niedozwolonych znaków i zastępowanie ich bezpiecznymi. W przypadku pól do logowania należy z góry określić dopuszczalna długość słowa i czy nie zawiera znaków apostrofu i innych oprócz cyfr i liter.

Gdy hasłem są tylko cyfry można skorzystać z gotowej instrukcji PHP:

if (!ereg('^\d+$', $id))

Do usuwania pojedynczych apostrofów warto użyć dedykowanej instrukcji, np. dla MySQL przykładowy kod wygląda następująco:

$password = mysql_escape_string($password);

Każde okienko formularza może być wykorzystane do wklejania kodu HTML lub skryptów. Jeśli spodziewamy się tu tylko zwykłego tekstu można, jego zawartość oczyścić z tagów HTML przy użyciu gotowych instrukcji htmlspecialchars Składnia tego polecenia to:

string htmlspecialchars ( string $string [, int $tryb [, string $zestaw_znaków [, bool $podwójne_kodowanie ]]] )

Instrukcja zmienia znaki specjalne (<,>,',",&) na bezpieczne znaczniki kodu w nieaktywnej postaci. Takie tłumaczenie wprowadzanych tekstów zabezpiecza stronę również przed atakiem XSS - Cross Site Scripting.

Dodatkowym zabezpieczeniem przed niebezpiecznymi danymi wprowadzanymi z zewnątrz jest skorzystanie z funkcji serwera: mod_rewrite która pozwala przekierować wszystkie przychodzące żądania na inny adres na serwerze.

Adres URL z widocznymi parametrami:

http://www.domena.pl/zakupy.php?kategoria=6&produkt=119

adres przepisany z zastosowaniem funkcji: mod_rewrite

http://www.domena.pl/zakupy,6,119.html

Kod wykonuje się wtedy poza wglądem użytkownika, nie widzi on wszystkich parametrów w adresie i ma ograniczone możliwości ingerowania. Dopuszczalne znaki jakie można wpisać w adresie są testowane przy użyciu wyrażeń regularnych.