SQL Injection może być jednym z najgroźniejszych a zarazem najbardziej popularnych ataków na aplikacje webowe, strony i sklepy internetowe. Niemal każda obecnie dynamiczna strona internetowa przechowuje swoje dane w bazie danych SQL. Jeżeli aplikacja przyjmuje dane wejściowe, które są zapisywane w bazie danych lub na ich podstawie generowane są zestawienia danych staje się potencjalną ofiarą ataku SQL Injection.
Gdzie dochodzi do ataku SQL Injection?
Do ataku SQL Injection może dochodzić we wszystkich dynamicznych aplikacjach webowych gdzie przyjmuje się dane wejściowe lub używa się zmiennych GET w adresach URL, których wartości są bezpośrednio używane do odpytywania bazy danych. Mogą to być zatem:
- wewnętrzna wyszukiwarka,
- filtry w listingach (produktów, ogłoszeń)
- fora internetowe, sekcje komentarzy,
- formularze kontaktowe, rejestracji i logowania, pop-upy subskrypcji.
Na czym polega atak SQL Injection?
Mechanizm działania ataku polegającego na wstrzyknięciu instrukcji SQL jest tak naprawdę bardzo prosty. Zakłada się, że dane wejściowe pochodzące np. z formularzy lub zmiennych GET bez obróbki i filtrowania będą dołączone do ciągu stanowiącego jakieś zapytanie SQL.
Jak wiemy w języku SQL poza instrukcjami odczytu można użyć instrukcje zapisu a także usuwania danych. Za pomocą krótkich instrukcji w języku SQL można wyczyścić bądź usunąć całą bazę danych.
Zaawansowany atak SQL Injection może wykorzystywać luki w funkcjach, których użyto do filtrowania danych wejściowych. Tak jak przy innych atakach najlepiej wszystko zilustrować na prostym przykładzie.
Przykład ataku SQL Injection z wykorzystaniem formularza
Dla celów dydaktycznych załóżmy, że mamy mechanizm logowania w PHP, który prosi użytkownika o wpisanie e-maila (lub nazwy użytkownika) i hasła.
Wpisując w pole z e-mailem:
adres@email.pl” —
Wygenerowane zapytanie będzie wyglądało mniej-więcej tak:
SELECT * FROM users WHERE user_email = „adres@email.pl” –” AND password = „…”
Problem w tym, że znak — (podwojony myślnik) to początek komentarza w SQL. W przypadku mało dopracowanych mechanizmów (takich jak ten przykładowy) taka luka może umożliwiać ominięcie konieczności wpisania hasła ponieważ rzeczywiste zapytanie to:
SELECT * FROM users WHERE user_email = „adres@email.pl”
W praktyce mało kto stosuje takie mechanizmy ale jest on teoretycznie możliwy i takie logowanie często można spotkać w różnego rodzaju „kursach online” programowania w PHP i MySQL.
Jeżeli stworzylibyśmy mechanizm, który wymaga weryfikacji skrótu hasła przechowywanego w bazie:
<?php if (isset($_POST["login"])){ $l = $_POST["login"]; $p = $_POST["password"]; $link = mysqli_connect("localhost", "root", "", "test"); $query = mysqli_query($link, "SELECT * FROM users WHERE login = '$l'"); if(mysqli_num_rows($query) > 0) { $record = mysqli_fetch_assoc($query); if (password_verify($p, $record["password"])){ echo "Dane OK, następuje logowanie"; } else{ echo "Nieprawidłowy login lub hasło"; } } else{ echo "Nieprawidłowy login lub hasło"; } } ?> <form method="POST"> <input type="text" name="login"> <input type="password" name="password"> <input type="submit" name="submit" value="Zaloguj"> </form>
Istniejąca luka SQL i tak nie pozwoliła by się zalogować atakującemu – tak czy inaczej, z zasady należy luk SQL Injection unikać.
Inne przykłady ataku SQL Injection
Atakujący dodaje do nazwy użytkownika dodatkowe znaki aby z zapytania zrobiło się inne zapytanie, które wykona dodatkowy kod. Kiedy za nazwę użytkownika wstawimy taki ciąg:
test'; TRUNCATE TABLE users; SELECT * FROM users WHERE user = 'qwe
powstaną trzy osobne instrukcje, a ta środkowa usuwa tabelę.
SELECT * FROM users WHERE login = 'test'; TRUNCATE TABLE users; SELECT * FROM users WHERE login = 'qwe'
Obecne biblioteki PHP mają zabezpieczenie chroniące przed wywołaniem więcej niż jednej komendy 🙂 Problem w tym, że niektórzy nadal korzystają ze starszego mysql_query a MySQL Injection wcale nie musi wykonywać więcej niż jednej komendy. Szkodliwy kod można spreparować bez wychodzenia z jednej instrukcji.
W przykładach SQL Injection często podaje się przykłady w stylu:
32 OR 1=1
,które zawsze zwraca prawdę,
Login: " or ""=" Hasło: " or ""="
które ma wykonać:
SELECT * FROM Users WHERE Name ="" or ""="" AND Pass ="" or ""=""
Zagrożenia i skutki luk SQL Injection
SQL Injection może spowodować:
- wyciek danych,
- nieautoryzowany dostęp do kont użytkowników,
- manipulacje i usuwanie danych w bazie.
Oraz wszelkie inne następstwa, które mogą być wynikiem manipulacji za pomocą SQL.
Jak zabezpieczyć aplikację PHP przed SQL Injection?
Skuteczne załatanie podatności na SQL Injection jest stosunkowo proste. Mamy do dyspozycji kilka strategii, które wyeliminują możliwość przedostania się szkodliwych instrukcji do ostatecznego zapytania SQL kierowanego do bazy. Dla zwiększonego bezpieczeństwa można stosować wiele technik na raz. Najczęściej powtarzana zasada brzmi: filtruj dane wejściowe i koduj dane wyjściowe.
Aby uchronić aplikację przed tego typu atakami można użyć spreparowanych instrukcji (ang. prepared statements). W przypadku aplikacji PHP będzie to użycie PDO z silnie typowanymi parametrami (za pomocą funkcji bindParam()).
Czytaj więcej w osobnym kursie PDO
Kolejną opcją przeciwdziałania atakom SQL Injection są procedury przechowywane (ang. stored procedures), które polegają na przechowywaniu kodu SQL procedur w bazie danych.
Istnieje także technika polegająca na walidowaniu danych wejściowych z wykorzystaniem białej listy dozwolonych wyrażeń (ang. white list), co eliminuje wykonanie się zapytania zawierającego wartości, która nie występuje na predefiniowanej liście.
Ostatnią metodą może być filtrowanie i stosowanie znaków ucieczki w ciągach wprowadzanych przez użytkownika. W bibliotece PDO dzieje się to automatycznie a w proceduralnym PHP robimy to za pomocą instrukcji mysqli_real_escape_string() oraz innych funkcji.
Podsumowanie
SQL Injection to jeden z najgroźniejszych typów ataków na aplikacje webowe. Stosowanie dobrych praktyk i najnowszego podejścia w programowaniu pozwala skutecznie się przed nim uchronić. Czytaj więcej o zabezpieczaniu aplikacji webowych przed atakiem SQL Injection w źródłach.
Odpowiedz lub skomentuj