Co to jest SQL?
Wyjaśnijmy najpierw, czym jest cały ten SQL i jak wygląda jego zastosowanie. SQL (Structured Query Language) to język zapytań służący komunikacji z bazami danych. Baza danych to natomiast uporządkowany zbiór informacji potrzebnych do prawidłowego funkcjonowania aplikacji. Mogą to być loginy i hasła użytkowników, produkty (w przypadku sklepów online), adresy, numery PESEL i tym podobne.
Zapytanie w języku SQL to instrukcja, którą administrator przekazuje bazie danych, by wydobyć z niej potrzebne informacje lub wprowadzić zmiany. Przykładowe zapytanie może wyglądać następująco:
Powyższe zapytanie zwróciłoby wartości z kolumn „Imie” i „Nazwisko” w tabeli „Klienci”, ale tylko te wiersze, dla których wartość kolumny „Kraj” brzmi „Polska”. Innymi słowy: zapytanie zwróciłoby imiona i nazwiska klientów z Polski.
Bazy danych i związany z nimi język SQL działają wokół Ciebie, choć ich nie widzisz. To one odpowiadają za to, że możesz swobodnie logować się do usług, wprowadzając login i hasło, albo korzystać z funkcji „szukaj” w katalogach sklepów internetowych. Ty wpisujesz odpowiednie zmienne, a reszta dzieje się za kulisami.
SQL injection – co to jest?
Definicja SQL injection
Technika SQL injection, w skrócie zwana SQLi, to metoda cyberataku, w której haker wykorzystuje lukę w zabezpieczeniach aplikacji internetowej. Atakujący „wstrzykuje” własny kod do zapytania w języku SQL, za którego pomocą aplikacja komunikuje się z bazą danych, w ten sposób uzyskując do niej nieautoryzowany dostęp. Stąd też wzięła się nazwa cyberataku: injection oznacza właśnie wstrzyknięcie.
W przypadku ataków SQLi ochrony nie stanowią zabezpieczenia w postaci firewalli, programów antywirusowych i tym podobne. Nie wykorzystują one bowiem błędów oprogramowania, ale błędy projektowe popełnione przez samych programistów i osoby odpowiedzialne za bezpieczeństwo aplikacji. Innymi słowy: programista zostawia otwartą furtkę, a haker po prostu przez nią przechodzi. Nie ma potrzeby przełamywać zabezpieczeń, jeśli te zwyczajnie nie istnieją.
Jak działa atak SQLi?
O co zatem chodzi w SQL injection? Atak ten wykorzystuje fakt, że w przypadku braku odpowiednich zabezpieczeń, możliwe jest takie wykorzystanie wbudowanych formularzy witryny, by „oszukać” ją i dodać własne zapytanie SQL do tego, którego aplikacja używa, by skomunikować się z bazą danych.
Dla przykładu, haker może wykorzystać funkcję logowania do usługi internetowej i zamiast hasła wprowadzić sprytnie zredagowane zapytanie SQL. Jeśli formularz nie jest zabezpieczony, aplikacja po prostu użyje wpisanego zapytania zamiast hasła i doda je do instrukcji, którą przekaże bazie danych.
Haker tym sposobem może zalogować się do witryny jako administrator, uzyskać listę wszystkich loginów i haseł zapisanych w bazie albo nawet… usunąć bazę danych, nie zyskując nic samemu, ale generując szkody dla właściciela aplikacji.
Ataki SQLi są przeprowadzane na poziomie formularzy wbudowanych w aplikacje webowe, czyli m.in.:
- w polach logowania,
- w wewnętrznych wyszukiwarkach produktów,
- w formularzach kontaktowych,
- w formularzach rejestracyjnych do newsletterów.
SQL injection jest typowym atakiem na część serwerową aplikacji. To znaczy, że jest skierowany przede wszystkim na serwer, a nie na klienta, i szkodzi w głównej mierze twórcy aplikacji. Odwrotnie działa na przykład atak XSS, którego celem jest przede wszystkim klient – jego dane, loginy, hasła, pliki cookie.
Atak typu SQL injection dotyczy wszystkich baz danych wykorzystujących SQL: Oracle, MySQL, Microsoft SQL Server i innych.
Rodzaje SQL injection
Ataki SQLi można podzielić na kilka typów i podtypów. Różnią się od siebie sposobem, w jaki napastnik wchodzi w interakcję z zapytaniami SQL przesyłanymi przez aplikację.
In-band SQLi (klasyczny SQLi)
Najprostszy i najpopularniejszy typ ataku SQLi. W tym przypadku napastnik wykorzystuje tylko jeden kanał do przeprowadzenia ataku i do uzyskania jego wyników. Na przykład: jeśli używa zwykłej przeglądarki internetowej do wstrzyknięcia zapytania SQL, ta sama przeglądarka bezpośrednio zwróci mu wyniki jego działań. Metoda dzieli się na dwa podtypy.
1. Error-based SQLi
Cechą charakterystyczną tego rodzaju ataku jest to, że napastnik celowo wywołuje błędy bazy danych (stąd nazwa ataku: error-based oznacza oparty na błędach). Błędy mogą dostarczyć mu wielu cennych informacji, takich jak typ i wersja bazy danych albo jej struktura. Informacje te mogą pomóc w ustaleniu dalszej strategii działania. Wiedząc, jak wygląda struktura bazy i aplikacji, atakujący może skorzystać z innych metod SQLi i tak skonstruować kolejne zapytania, by osiągnąć zamierzony cel.
2. Union-based SQLi
Ta metoda polega na wykorzystaniu polecenia UNION w języku SQL, by połączyć wyniki kilku zapytań w jeden. Jest uważana za jedną z najbardziej niebezpiecznych, bo przy braku odpowiednich zabezpieczeń umożliwia wydobycie z bazy danych właściwie wszystkich informacji, które są w niej przechowywane.
Inferential SQLi (Blind SQLi)
W tym rodzaju ataku napastnik nie widzi bezpośrednio wyników wstrzykiwanych zapytań (stąd słowo blind, czyli ślepy). Może jednak obserwować zachowanie bazy danych i na tej podstawie odtworzyć jej strukturę. Ta metoda wymaga poświęcenia większej ilości czasu, ale nie jest wcale mniej niebezpieczna. Również dzieli się na dwa podtypy.
1. Boolean-based (content-based) blind SQLi
W tym przypadku napastnik wykorzystuje wstrzykiwanie zapytań SQL po to, by zmusić aplikację do zwracania różnych wyników w zależności od tego, czy zapytanie daje wynik PRAWDA (TRUE) czy FAŁSZ (FALSE). To metoda często wykorzystywana przez hakerów do wstępnego badania, czy dana aplikacja jest podatna na ataki SQLi.
2. Time-based blind SQLi
Metoda podobna do powyższej, z tym że tutaj napastnik zmusza bazę danych do „odczekania” pewnego czasu (zazwyczaj kilku sekund) przed wysłaniem odpowiedzi. Czas odpowiedzi sugeruje atakującemu, czy zapytanie zwróciło wynik PRAWDA, czy FAŁSZ. Taka metoda pozwala na przykład odgadnąć nazwę tablicy literka po literce, choć jest to czasochłonne.
Out-of-band SQLi
W tym rodzaju SQL injection haker nie korzysta z jednego kanału do przeprowadzenia ataku i uzyskania jego wyników. Zamiast tego zmusza aplikację do przesyłania odpowiedzi do innego punktu końcowego, który znajduje się pod jego kontrolą.
SQL injection – przykłady użycia
Aplikacje, które nie są odpowiednio zabezpieczone, nie weryfikują tego, co użytkownicy wprowadzają w ich formularze. Hakerzy mogą więc nimi manipulować w dowolny sposób. Popularnym przykładem złośliwego użycia wbudowanego formularza aplikacji jest wydobywanie poufnych danych, takich jak hasła i loginy użytkowników.
Jak to działa? Haker może skorzystać z formularza logowania, ale zamiast wprowadzać swoje dane, wpisać frazy, które w połączeniu z zapytaniem SQL zawsze zwrócą wynik TRUE. Wystarczy, że zamiast loginu i hasła poda następującą frazę:
Zapytanie SQL wygenerowane przez aplikację i przesłane do bazy danych będzie wtedy wyglądało następująco:
Powyższe zapytanie wykorzystuje fakt, że w języku SQL fraza OR ""="" zawsze zwraca wartość TRUE. Wynikiem przesłania takiego zapytania będzie natomiast wyświetlenie wszystkich loginów i haseł zapisanych w bazie.
Oczywiście odpowiednio zabezpieczona aplikacja nie będzie podatna na tego rodzaju sztuczki. Metoda jednak sprawdza się w przypadku amatorskich usług, które nie są chronione pod kątem ataków SQLi.
W ekstremalnych sytuacjach, to znaczy w przypadku bardzo amatorskich i w ogóle niezabezpieczonych formularzy, możliwe jest nawet… zalogowanie się do aplikacji bez podawania loginu i hasła. Jak to możliwe?
Najprostszy możliwy formularz logowania po wprowadzeniu danych może generować takie zapytanie do bazy:
Zapytanie prosi bazę o zwrócenie użytkownika, którego login i hasło są identyczne ze zmiennymi podanymi w formularzu logowania. W zależności od dalszych instrukcji może dojść do autoryzacji. Zapytanie musi jednak zwrócić wartość TRUE, a więc podane zmienne muszą zgadzać się z tymi, które są w bazie – czyli login i hasło muszą być prawidłowe.
Ale co, jeśli zmanipuluje się formularz tak, by zwracał wartość TRUE nawet bez podania prawidłowych danych uwierzytelniających? Jeśli aplikacja nie jest zabezpieczona, można to zrobić. Wystarczy, na przykład, zamiast loginu wpisać frazę ‘OR 1=1; /*, a zamiast hasła: */—.
Podany w loginie argument „1 = 1” jest zawsze prawdziwy. Gwiazdki i ukośniki oznaczają natomiast otwarcie i zamknięcie komentarza w języku SQL. To, co znajduje się między nimi, nie jest brane pod uwagę podczas przetwarzania zapytania. W tym wypadku hasło zostanie więc kompletnie zignorowane, a całe zapytanie SQL sprowadzi się do sprawdzenia, czy argument „1 = 1” jest prawdziwy. A ponieważ jest – zapytanie zwróci wartość TRUE, a w dalszej kolejności dojdzie nawet do uwierzytelnienia użytkownika.
Innym, popularnym przykładem ataku SQLi jest użycie polecenia UNION, które scala wyniki kilku zapytań z rodzaju SELECT. Umiejętnie wykorzystane może pozwolić hakerowi „wzbogacić” główne zapytanie SQL o kilka dodatkowych. Tym sposobem atakujący jest w stanie zmusić bazę danych do przekazania wielu poufnych informacji, takich jak loginy, hasła, numery PESEL czy maile użytkowników.
SQLi w życiu
Najbardziej popularne ataki hakerskie obejmują DDoS, ransomware, ale i właśnie SQL injection. W historii było już wiele sytuacji, w których napastnicy posługiwali się tą metodą z mniejszym bądź większym sukcesem, a także kilka (na szczęście) w porę odkrytych luk w zabezpieczeniach. Oto kilka z nich:
- Włamanie do banku w Katarze. W 2016 roku turecka grupa hakerska użyła SQLi, by wykraść 2GB dokumentów i informacji o klientach z Narodowego Banku Kataru.
- Podatność na atak SQLi Tesli. W 2014 roku grupa pasjonatów cyberbezpieczeństwa odkryła, że jest w stanie wykorzystać atak SQL injection, by wykraść dane klientów Tesla Motors. Firma została poinformowana o problemie.
- Kiepskie zabezpieczenia w Fortnite. W 2019 naukowcy z Checkpoint Research odkryli podatność gry Fortnite na kilka zagrożeń, w tym wstrzyknięcia kodów SQL. Twórcy gry zostali poinformowani o niedopatrzeniach.
Co takiego można stracić w wyniku ataku SQLi?
Krótka i prosta odpowiedź na to pytanie brzmi: wszystko. Każda aplikacja internetowa opiera się na bazach danych. To tam są przechowywane poufne dane biznesowe, o klientach, pracownikach, kontrahentach oraz inne informacje. Zaatakowanie bazy danych naraża wszystkie te dane na to, że dostaną się w niepowołane ręce.
Haker umiejętnie wykorzystujący atak SQL injection może uzyskać dostęp do wszystkiego, co znajduje się w bazie danych. Nawet jeśli początkowo uda mu się dojść tylko do jej struktury, pozwoli mu tu na dowiedzenie się, jakie tabele i informacje się w niej znajdują. To z kolei cenne informacje umożliwiające zaplanowanie kolejnych kroków w celu pozyskania reszty zawartości.
Atak na bazę danych nie tylko umożliwia odczytanie jej zawartości, ale także modyfikację. W przypadku SQLi kradzież to niejedyne ryzyko – atakujący często usuwają dane lub zmieniają zawartość baz. Złośliwy haker może tak zaprojektować atak, by po prostu usunąć istotne informacje, takie jak loginy, hasła, numery kart kredytowych i maile użytkowników aplikacji. Może je również najpierw wykraść, a potem usunąć lub zmienić – z czystej złośliwości.
SQL injection – jak się zabezpieczyć przed atakiem?
Na całe szczęście, SQLi jest jednym z tych ataków, przed którymi można dość łatwo się uchronić. Jak wspominaliśmy na początku artykułu, antywirusy czy firewalle w tym wypadku do niczego się nie przydadzą (choć oczywiście nie należy z nich rezygnować!). Atak SQL injection po prostu wykorzystuje źle napisany kod aplikacji. Kluczem jest więc upewnienie się, że i kod, i baza danych zostały stworzone z zachowaniem dobrych praktyk i zasad bezpieczeństwa.
Segregacja danych
W przypadku baz danych warto kierować się tą samą zasadą, co w przypadku pieniędzy. Nie trzymaj całej swojej gotówki w jednym miejscu i nie przechowuj razem wszystkich ważnych danych.
Ataki SQLi mają to do siebie, że jeśli hakerowi uda się dostać do bazy, zagrożona będzie cała jej zawartość. Szkody będą jednak mniejsze, jeśli dostanie się na przykład tylko do informacji dotyczących produktów dostępnych w sklepie, a nie do danych osobowych klientów czy kontrahentów. Właśnie dlatego dąż do decentralizacji danych i nie przechowuj ich wszystkich w jednej bazie.
Rzutowanie danych
Rzutowanie (konwersja) danych oznacza przekształcanie ich na docelowy format. Zakłada, że nigdy nie wolno ufać użytkownikom aplikacji, że wprowadzą prawidłowe dane. Jeśli więc tworzysz formularz, w którym należało będzie podać wartość tekstową, zawczasu przygotuj się na to, że użytkownik może zechcieć spróbować wpisać tam cokolwiek innego – na przykład liczbę.
Konwersja pozwala na przekształcenie danych wejściowych na odpowiedni format i uniknięcie niektórych form ataków SQL injection.
Prepared statements, czyli gotowe zapytania
Korzystanie z tak zwanych prepared statements oznacza, że aplikacja, zamiast generować pojedyncze zapytanie SQL wedle potrzeby, buduje je z uprzednio przygotowanych części. Chodzi o to, by nie tworzyć całej instrukcji na raz, lecz rozdzielić ją na polecenie oraz argumenty, które zostaną do niej dodane później. Zmienne w instrukcji są w tym wypadku zastępowane znakami zapytania.
Aplikacja korzystająca z prepared statements używa wcześniej zaprojektowanych instrukcji w języku SQL do komunikowania się z bazą danych. Zmienne wprowadzane przez użytkowników (loginy, frazy wyszukiwania itp.) są później dodawane w odpowiednie miejsca. Szkielet instrukcji pozostaje niezmienny. Nie ma więc możliwości zmodyfikowania go z poziomu formularza na stronie. Ta metoda chroni przed większością ataków SQLi, choć – w niektórych przypadkach – może negatywnie wpływać na wydajność aplikacji.
Sprawdzanie poprawności danych wejściowych (sanityzacja)
Sanityzacja danych wejściowych to coś, co powinno znajdować się na poziomie back-endu każdej porządnie zabezpieczonej aplikacji webowej. Polega na tym, że aplikacja przechowuje listę fraz i znaków, które wolno jej zaakceptować podczas przetwarzania formularza. Jeśli użytkownik spróbuje wpisać coś, co nie znajduje się na liście (np. komendę w języku SQL), powinno to być automatycznie odrzucone przez system sprawdzania danych wejściowych.