Wróć na blog
wordpress bezpieczeństwo hosting devops

Jak naprawdę zabezpieczyć WordPressa na hostingu współdzielonym (DirectAdmin + LiteSpeed + CloudLinux)

Praktyczny poradnik hartowania WordPressa z poziomu konta klienta — bez roota. wp-config, .htaccess, uprawnienia plików, cron, gotowy skrypt i obrona w głąb krok po kroku.

Franciszek Sikora

Dla kogo jest ten poradnik? Dla każdego, kto ma WordPressa na hostingu współdzielonym i chce spać spokojnie — niezależnie od tego, czy dopiero zaczyna, czy zarządza kilkudziesięcioma stronami. Pokazuję wszystko z poziomu konta klienta, bez dostępu do roota. Na końcu znajdziesz gotowy skrypt, który większość roboty zrobi za Ciebie.


Najpierw o co w ogóle chodzi (dla laików)

Wyobraź sobie, że Twoja strona to dom. Włamywacz nie musi być geniuszem — wystarczy, że znajdzie jedne otwarte drzwi. W świecie WordPressa tymi „drzwiami” są najczęściej:

  • przestarzała wtyczka (zdecydowanie numer jeden — większość włamań to nie dziura w samym WordPressie, tylko w jakimś dodatku),
  • słabe hasło administratora,
  • plik wgrany przez formularz, który serwer da się namówić do uruchomienia,
  • panel logowania wystawiony na świat, w który boty walą hasłami non stop.

Dobra wiadomość: nie musisz być ekspertem od bezpieczeństwa. Wystarczy nałożyć kilka warstw ochrony — a złodziej, zamiast trafić na otwarte drzwi, napotka kolejne zamknięte. Każda warstwa to dla niego dodatkowy koszt i czas. W pewnym momencie po prostu się poddaje i idzie do łatwiejszej ofiary.

Model warstwowej obrony

To się nazywa obrona w głąb (ang. defense in depth). Nie ma jednej magicznej wtyczki, która załatwia wszystko. Jest kilka prostych nawyków, które razem dają solidną ochronę.


Co już robi za Ciebie dobry hosting

Jeśli Twój hosting stoi na CloudLinux + CageFS + Imunify360 + LiteSpeed Enterprise, to sporo zabezpieczeń masz „w cenie” — i nawet o tym nie wiesz:

TechnologiaCo robi (po ludzku)
CageFSZamyka każde konto w osobnej „klatce”. Nawet jeśli ktoś włamie się do sąsiada na tym samym serwerze, nie zobaczy Twoich plików.
CloudLinux LVEPrzydziela każdemu kontu limit CPU/RAM. Jeden zaatakowany WordPress nie położy całego serwera.
Imunify360Antywirus + firewall (WAF) w jednym. Skanuje pliki, blokuje znane ataki i boty robiące brute-force.
LiteSpeed EnterpriseSzybki serwer WWW, który czyta reguły .htaccess tak samo jak Apache — czyli wszystko poniżej zadziała.

Twoja rola to dołożyć warstwy, których hosting nie ustawi za Ciebie, bo dotyczą konkretnie Twojej strony. I właśnie o tym jest reszta poradnika.


Warstwa 1: Higiena — nudna, ale ratuje najczęściej

Zanim dotkniemy plików konfiguracyjnych, zacznij od podstaw. To brzmi banalnie, ale 80% włamań zaczyna się właśnie tutaj.

Aktualizuj. Wszystko. Zawsze.

WordPress, motywy i wtyczki. Przestarzała wtyczka to jak zostawienie klucza pod wycieraczką. Włącz aktualizacje automatyczne, a przynajmniej sprawdzaj raz w tygodniu.

Usuwaj to, czego nie używasz

Nieaktywna wtyczka nadal jest na serwerze i nadal może mieć dziurę. Dezaktywacja nie wystarczy — usuń ją całkowicie. To samo z motywami: zostaw aktywny plus jeden domyślny na wszelki wypadek, resztę kasuj.

Pozbądź się loginu „admin”

Pierwsze, co próbuje każdy bot, to login admin. Jeśli go masz — utwórz nowego administratora pod inną nazwą i stare konto skasuj.

Włącz uwierzytelnianie dwuskładnikowe (2FA)

To ten kod z aplikacji w telefonie. Nawet jeśli ktoś wykradnie Twoje hasło, bez telefonu się nie zaloguje. Wtyczki: Two-Factor albo WP 2FA. To jest jedna z najważniejszych rzeczy w całym poradniku.


Warstwa 2: wp-config.php — serce ustawień

wp-config.php to plik z konfiguracją WordPressa. Dodanie kilku linijek znacząco podnosi poprzeczkę. Otwórz go (jest w głównym katalogu strony) i dorzuć:

// Wyłącza wbudowany edytor plików w kółku administracyjnym.
// Jeśli ktoś przejmie konto admina, nie wklei złośliwego kodu przez przeglądarkę.
define('DISALLOW_FILE_EDIT', true);

// Wymusza logowanie do panelu tylko po HTTPS.
define('FORCE_SSL_ADMIN', true);

// Wyłącza tryb debugowania na produkcji (nie pokazuj błędów światu).
define('WP_DEBUG', false);

// Ogranicza liczbę zapisywanych wersji wpisu (mniej śmieci w bazie).
define('WP_POST_REVISIONS', 5);

// Automatyczne aktualizacje drobnych wydań bezpieczeństwa.
define('WP_AUTO_UPDATE_CORE', 'minor');

💡 Smaczek dla zaawansowanych: dodanie define('DISALLOW_FILE_MODS', true); całkowicie blokuje instalację i aktualizację wtyczek z panelu. To najmocniejsza pojedyncza linijka — atakujący nie wgra przez kokpit żadnego „dodatku” ze złośliwym kodem. Haczyk: aktualizacje musisz wtedy robić przez WP-CLI (pokażę niżej). Dla strony, którą zarządza klient nietechniczny, może być zbyt restrykcyjne — wtedy zostań przy samym DISALLOW_FILE_EDIT.

Na koniec zmień „sole” (tajne klucze szyfrujące sesje). Wygenerujesz je na api.wordpress.org/secret-key i podmienisz odpowiedni blok w wp-config.php. Efekt uboczny: wszyscy zostaną wylogowani — i o to chodzi, bo wyrzuca to ewentualnego intruza.


Warstwa 3: .htaccess — strażnik przy bramie

To plik z regułami, które LiteSpeed czyta przy każdym żądaniu. Tu dzieje się magia.

Najważniejsza reguła w całym poradniku

Większość włamań kończy się wgraniem „webshella” — pliku PHP, który daje atakującemu zdalną kontrolę. Wrzuca go najczęściej do folderu wp-content/uploads (tam, gdzie lądują Twoje zdjęcia). Jeśli zablokujesz uruchamianie PHP w tym folderze, taki plik staje się bezużytecznym tekstem.

Jak działa webshell i jak go zablokować

Utwórz plik wp-content/uploads/.htaccess o treści:

<FilesMatch "\.(php|php[0-9]|phtml|phar)$">
  Require all denied
</FilesMatch>

To samo wrzuć do wp-content/cache/.htaccess. Ta jedna reguła neutralizuje większość realnych ataków.

Reguły do głównego .htaccess

# Ochrona plików wrażliwych przed odczytem z zewnątrz
<FilesMatch "^(wp-config\.php|\.user\.ini|php\.ini|error_log|debug\.log|readme\.html|license\.txt)$">
  Require all denied
</FilesMatch>

# Nie pokazuj zawartości katalogów (gdy brak index.php)
Options -Indexes

# Zablokuj enumerację autorów (?author=1 zdradza loginy)
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{QUERY_STRING} (^|&)author=\d
  RewriteRule ^ - [F,L]
</IfModule>

# Wyłącz xmlrpc.php — stary mechanizm, częsty cel ataków.
# UWAGA: jeśli używasz Jetpack lub aplikacji mobilnej WordPress, ten blok pomiń.
<Files xmlrpc.php>
  Require all denied
</Files>

Nagłówki bezpieczeństwa

Mówią przeglądarce, jak ma się zachować — utrudniają m.in. podszywanie się pod Twoją stronę:

<IfModule mod_headers.c>
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Content-Type-Options "nosniff"
  Header always set Referrer-Policy "strict-origin-when-cross-origin"
  Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>

Zamknij panel logowania na kłódkę

wp-login.php to brama, w którą boty walą tysiącami prób. Najprościej w DirectAdmin → „Ochrona katalogów hasłem” załóż dodatkowe hasło na katalog. Wtedy przed ekranem logowania WordPressa pojawi się okienko przeglądarki — bot zatrzymuje się już tam, a LiteSpeed odbija go tanio bez angażowania PHP.


Warstwa 4: Uprawnienia plików

Na CloudLinuxie PHP działa jako Twój użytkownik, więc nigdy nie potrzebujesz uprawnień 777 (to znaczy „każdy może wszystko” — proszenie się o kłopoty). Zasada:

  • Katalogi: 755 — wchodzić może każdy, zapisywać tylko Ty.
  • Pliki: 644 — czytać może każdy, zmieniać tylko Ty.
  • wp-config.php: 400 — tylko Ty możesz odczytać, nikt inny.

Skrypt na końcu ustawi to automatycznie.


A co, jeśli mój hosting NIE ma Imunify360?

Nie każdy hosting ma cały ten komplet. Jeśli brakuje Ci antywirusa/WAF-a albo separacji kont, oto co możesz dołożyć sam i o co poprosić dostawcę.

Zamiast Imunify360 (warstwa aplikacji)

Skoro hosting nie filtruje ataków na poziomie sieci, przenieś tę rolę do WordPressa:

  • Wtyczka typu WAF + skaner — np. Wordfence (darmowa wersja ma firewall i skaner malware) albo Sucuri. ⚠️ Ale jeśli hosting ma Imunify360, nie włączaj drugiego firewalla — dublują się i zjadają limity. Wybierz jedno.
  • Limit prób logowania — wtyczka Limit Login Attempts Reloaded. Po kilku błędnych próbach blokuje IP. To Twój ręczny zamiennik ochrony brute-force z Imunify.
  • Skan integralności z WP-CLI (patrz cron niżej) — własny, darmowy „wykrywacz” podmienionych plików.

Separacja domen przez open_basedir (gdy brak CageFS)

To prośba do hostingodawcy, ale warto wiedzieć, o co pytać. open_basedir to ustawienie PHP, które mówi: „ten kod ma prawo czytać tylko ze swojego katalogu i nigdzie indziej”. Bez CageFS to najważniejsza bariera między kontami/domenami.

CageFS vs open_basedir

Jak widać na obrazku, open_basedir jest słabszy od CageFS (działa tylko na warstwie PHP, współdzielone /tmp czy /proc zostają), ale to wciąż ogromna różnica w porównaniu z brakiem jakiejkolwiek separacji.

Dla hostingodawcy — przykładowa konfiguracja per domena (np. w wirtualnym hoście LiteSpeed/Apache albo w php.ini per użytkownik):

; Każda domena widzi tylko swój katalog + własny katalog na pliki tymczasowe
open_basedir = "/home/user1/domains/example.com/:/home/user1/tmp/"

; Osobny, prywatny katalog na sesje i pliki tymczasowe (zamiast wspólnego /tmp)
session.save_path = "/home/user1/tmp/sessions"
upload_tmp_dir = "/home/user1/tmp/uploads"
sys_temp_dir = "/home/user1/tmp"

W LiteSpeed Enterprise to samo można podać na poziomie Virtual Host → Context, a w DirectAdmin z CustomBuild — przez szablony php.ini per użytkownik. Kluczowe, żeby tmp był osobny dla każdego konta — wspólny /tmp to klasyczna droga przeskoku między domenami.

🔧 Smaczek dla hostingodawców: sam open_basedir da się obejść przez niektóre funkcje systemowe PHP. Dlatego razem z nim wyłącz niebezpieczne funkcje:

disable_functions = exec,passthru,shell_exec,system,proc_open,popen,show_source,pcntl_exec
expose_php = Off
allow_url_include = Off

Uwaga: część wtyczek backupowych potrzebuje exec — komunikuj zmiany klientom albo wprowadzaj je z whitelistą.

Minimum, o które warto poprosić dostawcę bez Imunify360

  1. ModSecurity z regułami OWASP (darmowy WAF na poziomie serwera).
  2. open_basedir per domena + osobny tmp (jak wyżej).
  3. disable_functions dla niebezpiecznych funkcji PHP.
  4. suEXEC/suPHP — żeby PHP działało jako Twój użytkownik, nie jako wspólny www-data.
  5. Regularne, zewnętrzne backupy — kopia na tym samym serwerze to nie backup.

Warstwa 5: Cron i monitoring — żeby nie pilnować ręcznie

Skoro wyłączyliśmy „WP-Cron na żądanie” (DISABLE_WP_CRON), trzeba ustawić prawdziwego crona w DirectAdmin (zakładka Zadania Cron).

# Uruchamia zaplanowane zadania WordPressa co 10 minut (zamiast przy każdym wejściu)
*/10 * * * * /usr/local/bin/php /home/USER/domains/example.com/public_html/wp-cron.php >/dev/null 2>&1

# Codzienne automatyczne aktualizacje (przydatne zwłaszcza z DISALLOW_FILE_MODS)
0 4 * * * cd /home/USER/domains/example.com/public_html && wp plugin update --all && wp theme update --all

# Codzienna kontrola integralności + alert mailem, jeśli ktoś podmienił pliki WordPressa
0 5 * * * cd /home/USER/domains/example.com/public_html && wp core verify-checksums || echo "ALERT: zmienione pliki core!" | mail -s "WP checksum FAIL" ty@example.com

To ostatnie zadanie to Twój darmowy system wczesnego ostrzegania — jeśli ktoś podmieni plik WordPressa, dostaniesz maila.


Gotowy skrypt — zrób to wszystko jednym poleceniem

Zamiast klikać ręcznie, możesz puścić skrypt, który przeleci przez instalację i ustawi większość powyższego. Działa z poziomu Twojego konta — bez roota.

Krok 1: Zainstaluj WP-CLI (jeśli nie masz)

WP-CLI to narzędzie do zarządzania WordPressem z linii poleceń. Instalacja bez roota:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
mkdir -p ~/bin && mv wp-cli.phar ~/bin/wp
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc && source ~/.bashrc
wp --info   # sprawdzenie

Krok 2: Uruchom skrypt

Najpierw na sucho (dry-run) — pokaże, co zrobi, ale niczego nie zmieni:

./wp-hardening.sh /home/USER/domains/example.com/public_html

Gdy przejrzysz raport i wszystko wygląda OK — zastosuj zmiany:

./wp-hardening.sh /home/USER/domains/example.com/public_html --apply

Dodatkowe flagi:

FlagaDziałanie
--applyFaktycznie wprowadza zmiany (bez tego: tylko raport)
--lock-modsWłącza DISALLOW_FILE_MODS (pełna blokada instalacji z panelu)
--keep-xmlrpcNie blokuje xmlrpc.php (zostaw, jeśli używasz Jetpack / aplikacji mobilnej)
--no-headersPomija nagłówki bezpieczeństwa (gdy ustawiasz je gdzie indziej)

Skrypt najpierw robi backup wp-config.php, .htaccess i bazy danych do katalogu domowego, więc zawsze możesz się cofnąć. Pełny kod pobierzesz tutaj: wp-hardening.sh.


Ściąga — checklista na lodówkę

PODSTAWY (zrób dziś)
[ ] WordPress, motywy, wtyczki zaktualizowane
[ ] Usunięte nieużywane wtyczki i motywy
[ ] Brak konta o loginie „admin"
[ ] 2FA włączone dla każdego administratora
[ ] Silne, unikalne hasła

PLIKI
[ ] wp-config.php: DISALLOW_FILE_EDIT, FORCE_SSL_ADMIN, świeże sole
[ ] .htaccess w /uploads i /cache blokuje PHP
[ ] Uprawnienia: katalogi 755, pliki 644, wp-config 400

DOSTĘP
[ ] wp-login.php pod dodatkowym hasłem (DirectAdmin)
[ ] SFTP zamiast FTP; usunięte zbędne konta FTP
[ ] HTTPS wymuszony (Let's Encrypt)

AUTOMATYZACJA
[ ] Cron dla wp-cron.php (DISABLE_WP_CRON = true)
[ ] Cron z verify-checksums + alert mailem
[ ] Backupy + kopia POZA serwerem

BEZ IMUNIFY360
[ ] Wtyczka WAF/skaner (Wordfence/Sucuri) LUB ModSecurity od hosta
[ ] Limit prób logowania
[ ] open_basedir per domena + osobny tmp (poproś hosta)
[ ] disable_functions dla niebezpiecznych funkcji PHP

Na koniec — najważniejsza myśl

Bezpieczeństwo to nie jednorazowy projekt, tylko nawyk. Nie musisz zrobić wszystkiego naraz. Zacznij od pięciu rzeczy o największym znaczeniu:

  1. Aktualizacje (automatyczne).
  2. 2FA dla administratorów.
  3. Blokada PHP w /uploads (ta jedna reguła .htaccess).
  4. DISALLOW_FILE_EDIT w wp-config.php.
  5. Backupy poza serwerem.

To zajmie godzinę, a odetnie zdecydowaną większość realnych ataków. Resztę dokładaj warstwami — każda kolejna to dla atakującego kolejne zamknięte drzwi.