Krótko: Aby sprawdzić bazę danych pod kątem złośliwego kodu, przeszukują Państwo eksport lub kopię zapytaniami tylko do odczytu, szukając wstrzykniętych skryptów, obcych adresów i zaciemnionego kodu w treściach i ustawieniach. Należy też sprawdzić wyzwalacze, zaplanowane zdarzenia, procedury składowane i użytkowników bazy danych. Oczyszczać można dopiero po ocenie i nigdy przez ślepe wyszukiwanie i zamianę, ponieważ dane serializowane zostałyby uszkodzone.
Martwy punkt wielu oczyszczeń
Po włamaniu większość operatorów sprawdza pliki. Wymieniają rdzeń CMS, usuwają podejrzane pliki PHP i instalują rozszerzenia od nowa. Strona wygląda potem czysto, a tydzień później znów przekierowuje na obce witryny. Przyczyna leży często w bazie danych, którą podczas oczyszczania przejęto bez zmian.
Dla atakujących baza danych jest atrakcyjną kryjówką. Skrypt w widżecie jest dostarczany na każdej stronie. Zmienione ustawienie adresu przekierowuje wszystkich odwiedzających. Dodatkowe konto administratora otwiera drzwi na później. Nic z tego nie wychodzi na jaw przy skanie plików.
Po PHP2Shell, czyli lukach CVE-2026-109618 i CVE-2026-109619 umożliwiających wykonanie kodu bez logowania, baza danych należy więc do każdej kontroli. Kto może wykonać kod na serwerze, zazwyczaj może odczytać z konfiguracji dane dostępowe do bazy i dowolnie w niej zapisywać. Jak aplikacja w ogóle komunikuje się z bazą danych, wyjaśnia artykuł PHP i bazy danych: PDO i mysqli.
Gdzie złośliwy kod ukrywa się w bazie danych
| Miejsce | Typowa zawartość | Skutek |
|---|---|---|
| Treści (wpisy, strony, produkty) | bloki <script>, niewidoczne linki, <iframe> |
złośliwy kod lub spam dla odwiedzających |
| Ustawienia i opcje | zmieniony adres strony, obce skrypty w widżetach lub opcjach motywu | przekierowania, skrypty na wszystkich stronach |
| Tabele użytkowników | dodatkowi administratorzy | trwały dostęp |
| Wyzwalacze | kod, który przy każdym zapisie zmienia treści | złośliwy kod wraca po oczyszczeniu |
| Zaplanowane zdarzenia | cykliczne zadania na serwerze bazy danych | zmiany bez dostępu przez sieć WWW |
| Użytkownicy bazy danych | dodatkowe konta z szerokimi uprawnieniami | dostęp z pominięciem CMS |
Ostatnie trzy wiersze są szczególnie często pomijane, ponieważ nie leżą w tabelach CMS, lecz w samym serwerze bazy danych.
W sklepach internetowych jedno miejsce zasługuje na szczególną uwagę: ustawienia, za pomocą których do kasy i finalizacji zamówienia dołączane są dodatkowe skrypty. Obcy skrypt w tym miejscu może podejrzeć dane wpisywane przez klientów, zanim zostaną zaszyfrowane i przekazane do operatora płatności. Dlatego proszę celowo sprawdzić wszystkie pola na kod nagłówka i stopki, osadzenia śledzące oraz ustawienia projektu graficznego, które działają na stronach kasy.
Proszę też pamiętać o innych bazach danych na tym samym serwerze. Kopie testowe i stagingowe, stare wersje projektów lub bazy innych stron leżą często obok i są osiągalne tymi samymi danymi dostępowymi. To, co znajdzie się w bazie produkcyjnej, należy sprawdzić także tam, zanim kopia posłuży później znowu jako podstawa.
Sprawdzanie bazy danych pod kątem złośliwego kodu: treści i ustawienia
Proszę pracować na eksporcie. Jest on zarazem zabezpieczeniem dowodów i podstawą wyszukiwania. Eksport można przeszukać jako plik tekstowy bez obciążania działającej bazy danych:
# Utworzyć spójny eksport (dane dostępowe zostaną zapytane)
mysqldump --single-transaction --routines --triggers --events -u BENUTZER -p DATENBANK > export.sql
sha256sum export.sql > export.sql.sha256
# Zliczyć typowe wzorce w eksporcie
grep -oiE '<script[^>]{0,80}' export.sql | sort | uniq -c | sort -rn | head -30
grep -oiE '(eval\(|base64_decode|fromCharCode|document\.write|atob\()' export.sql | sort | uniq -c
grep -oiE 'https?://[a-z0-9.-]+' export.sql | sort | uniq -c | sort -rn | head -50
Ostatnie zapytanie wypisuje według częstotliwości wszystkie domeny występujące w bazie danych. Obce domeny, których nikt w zespole nie zna, są najważniejszymi kandydatami. Do celowanych zapytań w samej bazie danych nadają się polecenia SELECT. Przykład używa tabel WordPress; prefiks i nazwy tabel innych systemów podano w ich dokumentacji:
-- Ustawienia ze skryptami lub podejrzanymi funkcjami
SELECT option_name, LENGTH(option_value) AS laenge
FROM wp_options
WHERE option_value LIKE '%<script%'
OR option_value LIKE '%eval(%'
OR option_value LIKE '%base64_decode%'
ORDER BY laenge DESC;
Wpisy o bardzo dużej zawartości zasługują na szczególną uwagę. Tło dotyczące kryjówek specyficznych dla WordPress przedstawia poradnik Wyszukiwanie backdoorów w WordPress.
Wyzwalacze, zdarzenia i konta bazy danych
Wyzwalacze i zaplanowane zdarzenia nie są w wielu aplikacjach internetowych w ogóle przewidziane. Jeśli istnieją, ktoś powinien umieć wyjaśnić, dlaczego. Poniższe zapytania odczytują dane wyłącznie z tabel systemowych MySQL i MariaDB:
-- Wyzwalacze we wszystkich bazach danych
SELECT TRIGGER_SCHEMA, TRIGGER_NAME, EVENT_OBJECT_TABLE, CREATED
FROM information_schema.TRIGGERS;
-- Zaplanowane zdarzenia
SELECT EVENT_SCHEMA, EVENT_NAME, STATUS, CREATED, LAST_EXECUTED
FROM information_schema.EVENTS;
-- Procedury i funkcje składowane
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE, CREATED
FROM information_schema.ROUTINES
WHERE ROUTINE_SCHEMA NOT IN ('mysql', 'sys', 'information_schema', 'performance_schema');
-- Użytkownicy bazy danych
SELECT user, host FROM mysql.user ORDER BY user;
Jeśli aplikacja używa PostgreSQL lub innego systemu baz danych, tabele systemowe i polecenia nazywają się inaczej; zasada pozostaje ta sama. Do analizy zaleca się osobnego użytkownika bazy danych, który może tylko czytać. Dzięki temu nawet literówka w zapytaniu niczego nie zmieni, a kontrola nie zostawia śladów w danych.
Wyzwalacz, który przy zapisie wpisu dołącza kod, wyjaśnia, dlaczego złośliwy kod wraca po każdym oczyszczeniu. Nieznani użytkownicy bazy danych należą do kontroli kont tak samo jak konta administratorów w CMS; więcej w artykule Wyszukiwanie obcych kont administratorów w CMS i na serwerze.
Czyste oczyszczanie bez niszczenia danych
Oczyszczanie jest najbardziej delikatną częścią. Sprawdziła się następująca kolejność:
- Ocenić znaleziska: każde trafienie klasyfikuje się jako znane, nieszkodliwe i możliwe do wyjaśnienia lub obce. Kody śledzące i map są często uzasadnione.
- Zamknąć furtkę: doprowadzić PHP do naprawionej wersji (8.5.12, 8.4.27, 8.3.36, 8.2.35 lub potwierdzony backport), zrestartować wszystkie procesy robocze, zaktualizować aplikację.
- Usunąć celowo: obce treści oczyszczać przez aplikację lub narzędziami, które poprawnie traktują dane serializowane. Obce wyzwalacze, zdarzenia i konta usunąć.
- Zmienić dane dostępowe: nowe hasło do bazy danych wpisać w konfiguracji aplikacji, ograniczyć uprawnienia użytkownika aplikacji do własnej bazy danych.
- Sprawdzić ponownie: powtórzyć zapytania wyszukujące po kilku dniach.
Niczego nie należy zastępować przez wyszukiwanie i zamianę w eksporcie SQL. Serializowane dane PHP zapisują długość każdego ciągu znaków. Jeśli długość się zmieni, ustawienie staje się nieczytelne, a strona może przestać działać.
Przekierowania, które występują mimo czystej bazy danych, mają przyczynę w innej warstwie. Przy zawężaniu pomoże artykuł Strona przekierowuje na obce witryny.
Jak konkretnie pomaga PHP SOS?
- Zabezpieczamy bazę danych z sumą kontrolną i przeszukujemy eksport pod kątem skryptów, obcych domen i zaciemnionego kodu.
- Sprawdzamy wyzwalacze, zdarzenia, procedury i użytkowników bazy danych, którzy leżą poza CMS.
- Oczyszczamy znaleziska w ramach usuwania malware, nie uszkadzając danych serializowanych.
- Ograniczamy uprawnienia użytkownika aplikacji i powtarzamy kontrolę po oczyszczeniu.
Częste pytania
Czy złośliwy kod może siedzieć w bazie danych?
Tak. Atakujący wpisują JavaScript do wpisów, opisów produktów lub widżetów, zmieniają ustawienia adresów na potrzeby przekierowań albo zakładają konta administratorów. W MySQL i MariaDB dodatkowo wyzwalacze (triggery) i zaplanowane zdarzenia mogą zmieniać dane. Skan plików niczego z tego nie znajdzie.
Jak przeszukać bazę danych, niczego nie psując?
Proszę pracować na eksporcie lub kopii i używać wyłącznie zapytań SELECT. Eksport można ponadto przeszukać jako plik tekstowy za pomocą grep. Zmiany wprowadza się dopiero po ocenie każdego znaleziska i wykonaniu kopii zapasowej.
Dlaczego nie powinienem oczyszczać znalezisk przez wyszukiwanie i zamianę w eksporcie?
Wiele systemów CMS zapisuje ustawienia jako serializowane dane PHP, w których zapisywana jest też długość każdego ciągu znaków. Zamiana zmienia długość, ale nie zmienia zapisanej wartości, i ustawienie staje się nieczytelne. Oczyszczać należy przez aplikację lub narzędziami rozumiejącymi dane serializowane.
Czy każdy skrypt w bazie danych jest podejrzany?
Nie. Kody śledzące, mapy, filmy czy widżety czatu także są zapisywane jako skrypt. Decydujące jest, czy ktoś w zespole zna ten wpis i adres docelowy.