“Twoja strona zawiera oprogramowanie reklamowe lub złośliwe”.
To zdanie, wyświetlane na czerwonym ekranie przeglądarki (Google Safe Browsing) lub w mailu od hostingu, jest koszmarem każdego właściciela strony. Infekcja WordPressa to nie tylko kwestia wizerunkowa. To ryzyko kradzieży danych klientów, utrata pozycji w Google (tzw. “Japanese Keyword Hack” lub przekierowania na strony hazardowe) i potencjalne zablokowanie konta hostingowego.
Duża część ataków na WordPressa jest zautomatyzowana: oficjalny przewodnik hardeningu WordPressa zaznacza, że wiele ataków prowadzą samodzielnie boty. Boty nie atakują Ciebie osobiście, one skanują sieć w poszukiwaniu nieaktualnych wtyczek, słabych haseł czy luk w motywach.
W tym artykule pokażę Ci profesjonalny, techniczny proces czyszczenia zainfekowanego WordPressa. Zapomnij o “wtyczkach do usuwania wirusów”, które często maskują problem. Pokażę Ci, jak zrobić to ręcznie, skutecznie i na poziomie serwera, używając SSH i WP-CLI.
Jeśli pierwszym sygnałem były japońskie strony w Google, a nie cokolwiek widocznego na stronie, masz prawdopodobnie odmianę z cloakingiem, która wymaga innego pierwszego kroku: zobacz japoński hack słów kluczowych, zanim cokolwiek skasujesz.
Objawy włamania na WordPressa i co zapisać na początku
“Włamanie” to pojęcie nieostre. Strona FAQ My site was hacked w oficjalnej dokumentacji WordPressa nazywa konkretne objawy wskaźnikami kompromitacji (IoC) i wymienia te najbardziej jednoznaczne:
- strona trafiła na czarną listę Google, Binga lub innych wyszukiwarek;
- hosting wyłączył stronę;
- strona została oznaczona jako rozsyłająca malware;
- odwiedzający zgłaszają, że ich antywirus blokuje stronę;
- ktoś Cię zawiadomił, że Twoja strona służy do atakowania innych stron;
- pojawiły się działania, których nikt nie autoryzował, na przykład nowi użytkownicy;
- zmianę widać od razu po otwarciu strony w przeglądarce.
Pierwszym praktycznym krokiem według tej samej strony jest dokumentacja. Zanim cokolwiek usuniesz, zapisz, co widzisz, kiedy zauważyłeś problem (ze strefą czasową) i co ostatnio się zmieniło: nowa wtyczka, zmiana w motywie, edycja widżetu. Dopisz dane środowiska hostingowego (wersja PHP, typ serwera, kto ma dostęp). Ta notatka to podstawa raportu z incydentu i oszczędza czas, niezależnie od tego, czy czyścisz sam, czy przekazujesz pracę komuś innemu.
Dwa kroki z tego samego źródła często się pomija. Po pierwsze, przeskanuj antywirusem własny komputer: lokalny trojan, który przechwytuje dane logowania do FTP lub /wp-admin, czyni każde czyszczenie na serwerze bezużytecznym. Po drugie, skontaktuj się z hostingiem. Na hostingu współdzielonym infekcja może obejmować więcej niż Twoją stronę, a strona wysyłająca spam może wpisać IP serwera na czarne listy pocztowe, co uderza też w pocztę wychodzącą z tego serwera.
Kopia zapasowa zainfekowanego WordPressa przed czyszczeniem
Zanim dotkniesz czegokolwiek, zrób kopię zapasową stanu obecnego. Dlaczego? Ponieważ w trakcie czyszczenia możesz przez pomyłkę usunąć krytyczny plik (np. zmodyfikowany functions.php, który zawierał kluczową funkcjonalność, a nie tylko wirusa). Dokumentacja WordPressa zaleca tę kopię nawet przy zainfekowanej stronie: jeśli czyszczenie pójdzie źle, masz przynajmniej poprzedni stan do wglądu.
Jeśli masz dostęp do SSH:
## Spakuj wszystkie pliki
tar -czf backup-infected-files_$(date +%F).tar.gz .
## Zrób zrzut bazy danych
wp db export backup-infected-db_$(date +%F).sql --allow-rootJak znaleźć malware w WordPressie grepem
Zanim zaczniesz wycinać, musisz wiedzieć, z czym walczysz. Malware w WordPressie często ukrywa się pod postacią zaciemnionego kodu (obfuscated code) wykorzystującego funkcje takie jak eval, base64_decode, czy gzinflate. Raport problemów z bezpieczeństwem Google pokazuje typowy przykład, eval(base64_decode("...")), i radzi szukać fraz eval, base64_decode i unescape.
Oto lista przydatnych komend grep, które pozwolą Ci przeskanować pliki w poszukiwaniu podejrzanych ciągów znaków. Uruchom je w katalogu głównym strony.
Najczęstsze sygnatury malware
Szukanie funkcji
eval(bardzo rzadko używana legalnie w nowoczesnym kodzie):grep -rl --include=*.php 'eval(' .Szukanie ukrytych ramek i skryptów wstrzykniętych w pliki PHP:
grep -rl --include=*.php '<script' . grep -rl --include=*.php '<iframe' .Szukanie zakodowanych i skompresowanych ciągów (Base64, Rot13, gzinflate):
grep -rl --include=*.php 'base64_decode' . grep -rl --include=*.php 'str_rot13' . grep -rl --include=*.php 'gzinflate' .Szukanie
unescapew JavaScripcie:grep -rl --include=*.js 'unescape(' .
Nie dodawaj do tych wzorców opcji -w. Z -w grep wymaga, żeby dopasowanie kończyło się znakiem, który nie jest literą ani cyfrą, więc eval( nie znajdzie wtedy najczęstszej postaci, czyli eval(base64_decode(.
Jak odróżnić fałszywy alarm od malware
Nie każdy wynik to wirus.
- Wtyczki takie jak Elementor czy Visual Composer mogą mieć
base64_decodew swoim kodzie. - Zwracaj uwagę na pliki w dziwnych lokalizacjach, np.
wp-content/uploads/2024/03/avatar.php. W folderzeuploadsnie powinno być żadnych plików PHP.
Aby znaleźć wszystkie pliki PHP w folderze uploads:
find ./wp-content/uploads -name "*.php"Wszystko, co tu znajdziesz, traktuj jako podejrzane, dopóki nie udowodnisz, że jest inaczej. PHP w tym folderze trzyma tylko bardzo specyficzna wtyczka, a jej autor powinien to opisać w dokumentacji.
Jak zobaczyć zainfekowanego WordPressa tak, jak widzi go Google
Wiele ataków pokazuje spam tylko Googlebotowi albo osobom, które przychodzą z wyszukiwarki (cloaking). Właściciel wchodzi na stronę bezpośrednio i widzi, że wszystko jest w porządku. Raport problemów z bezpieczeństwem Google odradza otwieranie zainfekowanych stron w przeglądarce, bo malware może wykorzystać luki w samej przeglądarce, i podaje dwie alternatywy.
Pierwsza to narzędzie do sprawdzania adresów URL w Search Console, które pokazuje stronę tak, jak otrzymuje ją Google. Druga to curl (lub wget) z ustawionym refererem i user-agentem, żeby udawać gościa z wyników Google:
curl -sv --referer "https://www.google.com/" \
--user-agent "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
https://twoja-domena.pl/ -o strona.html
grep -iE 'eval|base64_decode|unescape|<iframe' strona.htmlPowtórz zapytanie bez referera i porównaj odpowiedzi. Kod 301 lub 302, który pojawia się tylko z refererem Google, wskazuje na reguły w .htaccess albo w kodzie PHP. Google wymienia trzy formy przekierowań używane przez atakujących: reguły w konfiguracji serwera (.htaccess, httpd.conf), przekierowania w JavaScripcie zależne od document.referrer i skrypty ładowane z obcej domeny.
Podmiana plików core WordPressa przez WP-CLI
Ręczne usuwanie wirusa z plików rdzenia WordPressa (wp-admin, wp-includes) to syzyfowa praca. Najszybszą i najpewniejszą metodą jest zastąpienie wszystkich plików systemowych czystymi oryginałami.
To bezpieczny proces, jeśli nie modyfikowałeś ręcznie plików rdzenia (czego i tak nie powinieneś robić).
Dokumentacja WordPressa każe instalować ponownie tę samą wersję, której używa strona: starsza lub nowsza może położyć serwis. Bez --version polecenie wp core download pobiera najnowszą wersję, więc wskaż bieżącą:
## Pobierz tę samą wersję WordPressa (zastępując pliki)
wp core download --version=$(wp core version --allow-root) --force --skip-content --allow-rootFlaga --skip-content jest kluczowa, sprawia, że WP-CLI nie nadpisze Twojego folderu wp-content, w którym są Twoje zdjęcia, motywy i wtyczki.
Jest tu ograniczenie, o którym trzeba wiedzieć. Według tej samej FAQ instalatory zwykle tylko nadpisują istniejące pliki, a ataki często dodają nowe. Dodatkowy plik w wp-includes przetrwa --force. Pokaże go weryfikacja sum kontrolnych opisana niżej.
Jeśli nie masz WP-CLI:
- Pobierz paczkę WordPress w tej samej wersji z wordpress.org.
- Rozpakuj ją lokalnie.
- Usuń z niej folder
wp-contentoraz plikwp-config-sample.php. - Na serwerze usuń foldery
wp-adminiwp-includes, wgraj te z paczki przez SFTP, a potem nadpisz pliki w katalogu głównym.
FAQ WordPressa odradza w tej sytuacji opcję ponownej instalacji w kokpicie z tego samego powodu: ona tylko nadpisuje.
Weryfikacja rdzenia i wtyczek przez wp core verify-checksums
WP-CLI porównuje zainstalowane pliki z sumami MD5 publikowanymi przez WordPress.org. Polecenie wp core verify-checksums działa na hooku before_wp_load, czyli nie ładuje WordPressa. Ma to znaczenie, gdy sam WordPress może być przejęty.
wp core verify-checksums --allow-rootPrzy czystym rdzeniu wynik brzmi:
Success: WordPress installation verifies against checksums.Przy zmienionym rdzeniu pojawiają się ostrzeżenia dla poszczególnych plików i błąd na końcu:
Warning: File doesn't verify against checksum: wp-includes/version.php
Warning: File should not exist: wp-includes/dodatkowy-plik.php
Error: WordPress installation doesn't verify against checksums.Po włamaniu najważniejsze jest ostrzeżenie “File should not exist”. Kod źródłowy polecenia pokazuje, że domyślnie szuka ono nadmiarowych plików w wp-admin, wp-includes i w plikach wp-*.php w katalogu głównym (poza wp-config.php). Z --include-root (od wersji 2.8.0) sprawdza cały katalog główny, z wyjątkiem .htaccess, .maintenance, wp-config.php i wp-content. Jeśli strona używa polskiej wersji WordPressa, dodaj --locale=pl_PL, bo inaczej polecenie zgłosi przetłumaczone pliki jako zmienione. Nazwa dodatkowy-plik.php w przykładzie to tylko znacznik: przy prawdziwym ataku plik ma nazwę, która wygląda na legalną.
Dla wtyczek istnieje odpowiednik wp plugin verify-checksums:
wp plugin verify-checksums --all --allow-rootPolecenie porównuje każdą wtyczkę z sumami WordPress.org i zgłasza pliki dodane (“File was added”) oraz zmienione (“Checksum does not match”). Dla wtyczek spoza katalogu WordPress.org, czyli większości płatnych, sum nie ma: WP-CLI wypisuje ostrzeżenie i pomija wtyczkę. Takie wtyczki trzeba podmienić na czystą paczkę od producenta.
Usuwanie wirusa z wp-content w WordPressie
To tutaj zazwyczaj leży problem.
Wtyczki i motywy
Jeśli to możliwe, usuń i zainstaluj ponownie wszystkie darmowe wtyczki. Wirusy często doklejają się do każdego pliku .js lub .php na serwerze.
## Reinstalacja wtyczki z repozytorium (np. contact-form-7)
wp plugin install contact-form-7 --force --allow-rootDla płatnych wtyczek musisz pobrać czyste paczki od producenta i podmienić pliki przez SFTP.
Sprawdź swój motyw. Jeśli używasz motywu potomnego (child theme), przejrzyj ręcznie functions.php i header.php. To ulubione miejsca dla złośliwego kodu. FAQ WordPressa dopisuje do tej listy index.php i footer.php, bo zmiana w nich wpływa na każde wyświetlenie strony.
Folder uploads
Jak wspomniano wcześniej, folder mediów (wp-content/uploads) powinien zawierać tylko obrazy, PDF-y i dokumenty. Radykalne czyszczenie (usuwa wszystkie skrypty PHP z uploads):
find ./wp-content/uploads -name "*.php" -deleteUwaga: Sprawdź wcześniej listę (bez -delete), aby upewnić się, że nie usuwasz czegoś legalnego.
Czyszczenie bazy WordPressa ze spamu i fałszywych adminów
Infekcje często nie kończą się na plikach. Wirusy wstrzykują spamerskie linki do treści postów lub tworzą fałszywych administratorów.
Zaloguj się do bazy danych (phpMyAdmin lub wp db cli) i sprawdź:
Fałszywi administratorzy:
SELECT * FROM wp_users;Szukaj użytkowników, których nie rozpoznajesz, często z domenami
.ru,.cnlub losowymi ciągami znaków. Usuń ich natychmiast.Spam w treści: Wirusy typu “Japanese Keyword Hack” wstrzykują tysiące stron z chińskimi/japońskimi znakami reklamującymi podróbki. Sprawdź tabelę
wp_posts:SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%<script%'; SELECT ID, post_title FROM wp_posts WHERE post_content LIKE '%eval(%';
Jeśli Twoja strona nagle ma tysiące nowych podstron w indeksie Google, musisz wyczyścić te wpisy z bazy, a następnie zgłosić mapę witryny do Google Search Console, aby przyspieszyć usunięcie śmieci z indeksu.
Lista administratorów WordPressa przez wp user list
Zapytanie SELECT * FROM wp_users pokazuje wszystkich użytkowników, ale nie ich role, które siedzą w wp_usermeta. Polecenie wp user list łączy obie tabele i filtruje po roli:
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --allow-rootPorównaj listę z osobami, które faktycznie mają dostęp. Kolumna user_registered pomaga powiązać nieznane konto z czasem incydentu zapisanym na początku. W instalacji multisite --network wypisuje użytkowników całej sieci (bez ról, bo ten sam użytkownik może mieć inną rolę na każdej stronie), więc sprawdź też superadministratorów:
wp super-admin list --allow-rootJeśli atakujący przejął Twoje konto administratora i nie możesz się zalogować, FAQ WordPressa opisuje wyjście: zaloguj się do bazy przez phpMyAdmin lub Adminer, zmień w tabeli wp_users adres e-mail swojego konta na taki, który kontrolujesz, i użyj linku do odzyskiwania hasła na ekranie logowania.
Sprawdzenie wp-config.php i .htaccess po włamaniu
Malware często modyfikuje te dwa kluczowe pliki.
wp-config.php: Otwórz go i sprawdź, czy nie ma tam dziwnych linii. Często dodawane są odwołania do plików tymczasowych:
include '\tmp\sess_dsf43...'; // PODEJRZANE!Porównaj swój plik z
wp-config-sample.php. Powinien zawierać tylko dane do bazy i definicje stałych..htaccess: Wirusy często dodają tu reguły, które przekierowują ruch z wyszukiwarek (np. jeśli User-Agent to Googlebot) na strony ze złośliwym oprogramowaniem, podczas gdy Ty (wchodząc bezpośrednio) widzisz normalną stronę. Jeśli nie masz pewności, wygeneruj domyślny plik .htaccess WordPressa:
# BEGIN WordPress <IfModule mod_rewrite.c> RewriteEngine On RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] RewriteBase / RewriteRule ^index\.php$ - [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule . /index.php [L] </IfModule> # END WordPress
FAQ WordPressa przypomina, że .htaccess nie leży tylko w katalogu głównym: może siedzieć także w innych katalogach tej samej instalacji. Znajdź wszystkie:
find . -name ".htaccess"Jak zabezpieczyć WordPressa po włamaniu
Wyczyściłeś stronę? Dobrze. Teraz zamknij drzwi, żeby włamywacz nie wrócił.
Zmień hasła:
- Do wszystkich kont administratorów.
- Do bazy danych.
- Do FTP/SFTP i panelu hostingu.
FAQ WordPressa podkreśla jeden szczegół: jeśli zmieniłeś hasła w chwili wykrycia ataku, zmień je jeszcze raz po potwierdzeniu, że strona jest czysta. Jeśli zmieniasz użytkownika lub hasło bazy danych, wpisz nowe wartości do
wp-config.php.Zmień klucze szyfrujące (salts): To wyloguje wszystkich użytkowników. W edycji
wp-config.phppodmień sekcję kluczy na nowe, wygenerowane tutaj: https://api.wordpress.org/secret-key/1.1/salt/ Lub użyj WP-CLI:wp config shuffle-saltsZablokuj edycję plików z poziomu kokpitu: Dodaj do
wp-config.php:define( 'DISALLOW_FILE_EDIT', true );To zapobiega sytuacji, w której haker, który zdobędzie hasło administratora, może edytować pliki motywu/wtyczek bezpośrednio z panelu. Przewodnik hardeningu wyjaśnia, że stała działa jak odebranie wszystkim użytkownikom uprawnień
edit_themes,edit_pluginsiedit_files, a edytor w kokpicie to często pierwsze narzędzie atakującego po zalogowaniu.Wyłącz XML-RPC: Jeśli nie używasz aplikacji mobilnej WordPress lub Jetpacka, XML-RPC to tylko dziura w bezpieczeństwie (używana do ataków DDoS i Brute Force). Dodaj do
.htaccess:<Files xmlrpc.php> order deny,allow deny from all </Files>To składnia Apache 2.2. Na Apache 2.4 bez modułu
mod_access_compatużyjRequire all deniedwewnątrz bloku<Files>.
Uprawnienia plików i użytkownik bazy danych po czyszczeniu
Przewodnik hardeningu podaje konkretne wartości. Katalogi 755, pliki 644, zapisywalne tylko przez konto użytkownika, do którego należy strona:
find /sciezka/do/wordpressa/ -type d -exec chmod 755 {} \;
find /sciezka/do/wordpressa/ -type f -exec chmod 644 {} \;Plik wp-config.php powinien być czytelny tylko dla Ciebie i serwera WWW, co zwykle oznacza 400 lub 440.
Ten sam przewodnik zaznacza, że do normalnej pracy użytkownik MySQL potrzebuje tylko SELECT, INSERT, UPDATE i DELETE. W tym samym akapicie ostrzega jednak, że odbieranie ALTER i podobnych uprawnień nie jest zalecane bez przetestowanego planu kopii zapasowych, bo część aktualizacji zmienia schemat bazy. Po włamaniu bardziej przydaje się inny krok: jeśli na jednym serwerze działa kilka stron, każda powinna mieć własną bazę i własnego użytkownika, żeby przejęta strona nie otwierała dostępu do pozostałych.
Na koniec logi. Przewodnik hardeningu opisuje je jako główne źródło wiedzy o tym, jak atakujący wszedł: logi dostępu serwera pokazują IP, godzinę i żądania, w tym próby brute force i dołączania plików. FAQ nazywa ten etap analizą powłamaniową, a jej celem jest zamknięcie drogi ataku, a nie tylko usunięcie objawów. Przewodnik zaleca też regularne kopie całej strony w zaufanym miejscu, bo atak wykryty kilka dni później porównasz tylko z kopią sprzed niego.
Jak sprawdzić, czy WordPress nie jest już zainfekowany
Czyszczenie kończy się dopiero wtedy, gdy strona przechodzi te same testy, które ujawniły problem. Przez kolejne dni powtarzaj komendy grep z początku artykułu, find ./wp-content/uploads -name "*.php" i wp core verify-checksums. Pusta lista w uploads to oczekiwany wynik. Nowy plik PHP w tym folderze oznacza, że furtka nadal jest otwarta.
Wróć też do bazy danych. Uruchom ponownie wp user list --role=administrator, żeby upewnić się, że nie pojawił się żaden nieznany administrator, i powtórz zapytania do wp_posts szukające <script i eval(. Porównaj wp-config.php i .htaccess z czystymi wersjami, bo to dwa pliki, które malware zmienia najczęściej.
Jeśli infekcja była typu Japanese Keyword Hack, po zgłoszeniu mapy witryny obserwuj indeks w Google Search Console i sprawdzaj, czy strony ze spamem znikają.
Jeśli któryś test nie przejdzie, nie powtarzaj po prostu czyszczenia. Najpierw znajdź nieaktualną wtyczkę, motyw nulled albo słabe hasło, przez które wszedł atakujący, a dopiero potem czyść ponownie.
Raport problemów z bezpieczeństwem i prośba o weryfikację w Search Console
Jeśli Google oznaczył stronę, ostrzeżenie zniknie dopiero po weryfikacji. Raport problemów z bezpieczeństwem w Search Console jest według Google źródłem rozstrzygającym: Safe Browsing pokazuje ostrzeżenia zależnie od kontekstu przeglądania, więc możesz nie zobaczyć ostrzeżenia we własnej przeglądarce. Raport dzieli problemy na trzy kategorie: treści zhakowane, złośliwe i niechciane oprogramowanie oraz socjotechnika.
Każdy problem ma przykładowe adresy URL. Google zaznacza, że to tylko próbka, a problem bez przykładów nie oznacza, że żadna strona nie jest dotknięta. Napraw problem w całym serwisie: poprawienie tylko części stron nie daje częściowego powrotu do wyników wyszukiwania. Jeśli raport wymienia kilka problemów, napraw wszystkie.
Gdy wszystkie są naprawione, użyj w raporcie opcji Request Review (prośba o weryfikację). Według Google dobra prośba robi trzy rzeczy: wyjaśnia dokładny problem, opisuje kroki podjęte w celu jego naprawy i dokumentuje rezultat. Tu jako dowód przydają się notatka z incydentu z początku i wyniki poleceń weryfikujących.
Weryfikacja trwa od kilku dni do kilku tygodni, a Google informuje mailem o przyjęciu prośby i o jej zakończeniu. Nie wysyłaj prośby ponownie, zanim zapadnie decyzja. Prośba wysłana, zanim problem został naprawiony, może opóźnić kolejną, a nawet sprawić, że strona zostanie oznaczona jako recydywista.
Jeśli niedawno kupiłeś stronę, która miała te problemy już wcześniej, Google prosi, żeby napisać o tym w prośbie o weryfikację, po naprawieniu wszystkiego, co wymienia raport.
Podsumowanie
Walka z malware to wyścig zbrojeń. Najlepszą ochroną nie jest jednak “magiczna wtyczka security”, ale higiena cyfrowa:
- Błyskawiczne aktualizacje WordPressa i wtyczek.
- Nieinstalowanie “nulled” (pirackich) wtyczek i motywów, które są częstym źródłem infekcji.
- Posiadanie kopii zapasowej poza serwerem (np. w chmurze).
Jeśli czujesz, że techniczny aspekt Cię przerasta, lepiej zleć to specjalistom. Źle przeprowadzone czyszczenie może zostawić furtkę (backdoor), przez którą haker wróci po kilku dniach, a Ty stracisz kolejne pieniądze i zaufanie w oczach Google.
Zobacz nasz audyt bezpieczeństwa WordPress, jeśli chcesz uporządkować ryzyka i zabezpieczenia serwisu.







