blind SQL injection
| Po angielsku | blind SQL injection |
|---|---|
| Którą formą pisać | po angielsku Polski odpowiednik istnieje, ale w praktyce nikt tak nie pisze. Zostaw angielski, polski trzymaj jako synonim. |
| Inne formy | Blind SQLi · ślepy SQL injection · ślepe wstrzyknięcie SQL |
| Kategoria | Podatności i klasy błędów |
Blind SQL injection to odmiana SQL injection, w której aplikacja nie pokazuje ani wyniku wstrzykniętego zapytania, ani komunikatu o błędzie bazy. OWASP opisuje ją jako atak, który zadaje bazie danych pytania typu prawda albo fałsz i ustala odpowiedź na podstawie zachowania aplikacji [1]. MITRE ujmuje to jako sposób na obejście braku komunikatów o błędach [2]. Źródłem problemu jest ta sama słabość co w klasycznym wstrzyknięciu: CWE-89, czyli budowanie polecenia SQL z danych z zewnątrz bez neutralizacji znaków specjalnych [3].
Jak działa
Punkt wyjścia jest taki sam jak przy zwykłym SQL injection: parametr żądania trafia do zapytania jako fragment kodu SQL, a nie jako wartość. Różnica leży w kanale zwrotnym. Według OWASP typowa sytuacja to aplikacja skonfigurowana tak, by pokazywać ogólne strony błędów, w której jednak nie usunięto samego podatnego kodu [1]. Ukrycie błędu zmienia tylko to, co widzi atakujący. Zapytanie nadal wykonuje się z jego fragmentem.
Atakujący nie może więc przeczytać danych wprost. Zamiast tego dokleja do zapytania warunek logiczny i obserwuje skutek. Jeśli strona wygląda inaczej przy warunku prawdziwym niż przy fałszywym, każda odpowiedź daje jeden bit informacji [1]. Powtarzając pytania o kolejne znaki, można odtworzyć nazwę tabeli, login albo skrót hasła. MITRE opisuje ten przebieg w trzech fazach: rozpoznanie struktury zapytania i punktu wstrzyknięcia, ustalenie rodzaju bazy przez warunki specyficzne dla danego silnika, a na końcu wydobywanie schematu i danych pytaniami tak albo nie [2].
Schemat poniżej pokazuje jedną rundę takiego odpytywania.
Taki atak wymaga wielu żądań, dlatego w praktyce prowadzi się go narzędziami automatycznymi. MITRE ocenia prawdopodobieństwo ataku i jego typową wagę jako wysokie, a wymagane umiejętności jako średnie [2].
Odmiany
OWASP wyróżnia dwie techniki odczytu odpowiedzi [1]. Obie korzystają z tej samej podatności, różnią się tylko tym, co atakujący mierzy.
| Odmiana | Co mierzy atakujący | Przykład sygnału |
|---|---|---|
| boolean-based (na podstawie treści) | różnicę w treści strony przy warunku prawdziwym i fałszywym [1] | wpis wyświetlony albo pusta lista |
| time-based (na podstawie czasu) | czas odpowiedzi serwera [1] | odpowiedź opóźniona o kilka sekund |
W wariancie czasowym warunek jest połączony z poleceniem, które celowo spowalnia bazę. OWASP podaje jako przykłady funkcję BENCHMARK() w MySQL, WAITFOR DELAY w MS SQL i pg_sleep() w PostgreSQL [1]. MITRE wymienia opóźnienie typu WAITFOR DELAY także jako sposób wykrywania samej podatności na etapie rozpoznania [2]. Technika czasowa działa nawet wtedy, gdy strona zwraca zawsze identyczną treść.
W WordPressie
WordPress korzysta z bazy MySQL albo MariaDB przez obiekt $wpdb. Błędy prowadzące do blind SQL injection pojawiają się zwykle we wtyczkach i motywach, które same składają zapytania; zob. podatność we wtyczce i podatność w motywie.
Funkcja esc_sql() nie wystarcza. Dokumentacja podaje, że esc_sql() zabezpiecza wyłącznie wartości umieszczane w zapytaniu w cudzysłowie [5]. Jeśli wartość nie jest ujęta w cudzysłów, kod nadal jest podatny; dokumentacja wprost wskazuje przykład klauzuli ORDER BY z wstawioną wartością [5]. Funkcja nie chroni też liczb bez cudzysłowu, nazw pól ani słów kluczowych SQL [5].
Dokładnie ten błąd opisał Sekurak na przykładzie wtyczki WordPress SEO by Yoast w wersji 1.7.3.3 [6]. Parametr orderby przechodził przez sanitize_text_field() i esc_sql(), a mimo to atakujący mógł przeprowadzić blind SQL injection [6]. Artykuł opisuje potwierdzanie wstrzyknięcia funkcją opóźniającą, czyli wariant czasowy [6].
Zapytanie przygotowane. Zalecaną drogą jest $wpdb->prepare(), który według dokumentacji przygotowuje zapytanie SQL do bezpiecznego wykonania, w składni podobnej do sprintf() [4]. Obsługuje symbole zastępcze %d, %f, %s oraz %i dla identyfikatorów, czyli nazw tabel i pól, dostępny od WordPressa 6.2.0 [4]. Dokumentacja esc_sql() także odsyła do prepare() jako zwykle właściwego wyboru [5]. Kierunku sortowania i nazwy kolumny najlepiej nie przepuszczać wcale, tylko wybrać z listy dozwolonych wartości.
$allowed = array( 'post_title', 'post_date' );
$orderby = in_array( $_GET['orderby'] ?? '', $allowed, true ) ? $_GET['orderby'] : 'post_title';
$rows = $wpdb->get_results(
$wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_status = %s ORDER BY %i", 'publish', $orderby )
);
Przy zapytaniach z LIKE znaki wieloznaczne trzeba przekazać w argumencie, a samą frazę przepuścić przez $wpdb->esc_like() [4].
Zagrożenia i skutki
Brak widocznego wyniku nie zmniejsza zakresu dostępu. MITRE podaje, że po wydobyciu informacji o schemacie atakujący może przejść do usuwania tabel albo modyfikowania danych [2]. W WordPressie najcenniejsza jest tabela użytkowników.
- Eksfiltracja danych osobowych klientów, zamówień i adresów e-mail, co może oznaczać naruszenie ochrony danych osobowych.
- Odczyt skrótów haseł i ich łamanie offline, a potem przejęcie konta administratora.
- Odczyt tokenów i kluczy zapisanych w opcjach wtyczek.
- Zmiana danych, np. dopisanie uprawnień, prowadząca do eskalacji uprawnień.
Ponieważ każde żądanie daje tylko jeden bit odpowiedzi [1], atak wymaga wielu żądań i zostawia wyraźny ślad w logach serwera. To jedna z niewielu jego słabych stron.
Jak się bronić
Podstawą jest zapytanie parametryzowane. CWE-89 wymienia instrukcje przygotowane, zapytania parametryzowane i procedury składowane jako główną obronę, bo oddzielają kod od danych [3]. Ukrywanie komunikatów błędów nie usuwa podatności, co OWASP pokazuje na przykładzie stron z ogólnym komunikatem [1]; mimo to MITRE zaleca obsługę błędów, która nie ujawnia szczegółów bazy [2], bo tryb debugowania na produkcji ułatwia każdy wariant ataku.
- Walidacja wejścia z listą dozwolonych wartości jako warstwa dodatkowa [3], uzupełniona sanityzacją.
- Najmniejsze uprawnienia konta bazy danych: CWE-89 zaleca osobne konta o minimalnych prawach dla poszczególnych zadań [3].
- WAF i wirtualne patchowanie (virtual patching) jako obrona do czasu aktualizacji. W opisanym przypadku Yoast Sekurak pokazał regułę ModSecurity, która blokowała przecinek w parametrze
orderby[6]. - rate limiting i monitorowanie logów pod kątem serii podobnych żądań.
- Szybka aktualizacja wtyczek po publikacji poprawki.
Historia i przykłady
Klasyfikacje traktują blind SQL injection jako osobny wzorzec ataku. W katalogu CAPEC ma numer 7 i jest powiązany ze słabością CWE-89 [2]. Sama CWE-89 według MITRE regularnie trafia do zestawień CWE Top 25 [3].
W ekosystemie WordPressa znanym przykładem jest wspomniany błąd wtyczki WordPress SEO by Yoast 1.7.3.3, ujawniony 11 marca 2015 roku; Sekurak opisał go w czerwcu 2016 roku jako ilustrację wirtualnych poprawek [6]. Przypadek dobrze pokazuje, że użycie funkcji o nazwie kojarzonej z bezpieczeństwem nie oznacza jeszcze bezpiecznego zapytania.
Przypisy
6- [1] Blind SQL Injection · OWASP
- [2] CAPEC-7: Blind SQL Injection · MITRE
- [3] CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection') · MITRE
- [4] wpdb::prepare() — Code Reference · WordPress.org
- [5] esc_sql() — Code Reference · WordPress.org
- [6] Ochrona podatnych aplikacji webowych za pomocą wirtualnych poprawek w ModSecurity · Sekurak
Hasła w tym artykule
18- SQL injection
- podatność we wtyczce
- podatność w motywie
- eksfiltracja danych
- naruszenie ochrony danych osobowych
- łamanie haseł
- przejęcie konta
- token sesyjny
- eskalacja uprawnień
- zapytanie parametryzowane
- tryb debugowania na produkcji
- sanityzacja danych wejściowych
- zasada najmniejszych uprawnień
- WAF
- wirtualne patchowanie (virtual patching)
- ModSecurity
- rate limiting
- aktualizacja oprogramowania