W poprzednim artykule z serii Bezpieczeństwo WordPressa opisywałem najważniejsze zasady, jakimi powinniśmy kierować się podczas instalacji WordPressa, podczas korzystania i instalowania wtyczek oraz motywów. Jeżeli jeszcze nie przeczytałeś części pierwszej to koniecznie wróć do artykułu.

W części drugiej zajmiemy się bezpieczeństwem od kuchni czyli wszystkim tym co robimy samodzielnie. Przypomnę tylko, że odradzam osobiście stosowanie wszelkiego typu pluginów zabezpieczających typu “one click”, które dają często złudne poczucie bezpieczeństwa, a przy próbie odinstalowania powodują problemy ze stroną. Spokojnie możemy samodzielnie zrobić wiele, aby dobrze zabezpieczyć swojego WordPressa.

Zabezpieczenie zaplecza przed wścibskimi robotami i crackerami

Na pewno gdybym zapytał o to czy warto zmieniać nazwę ścieżki “wp-admin” na coś innego zdecydowana większość odpowie od razu “Tak!”. Niestety nie jest to zerojedynkowe. Poprawna odpowiedź na to pytanie brzmi “i tak i nie”. Jeżeli ktoś panicznie boi się, że jakiś robot czy niechciana osoba dostanie się do zaplecza to tak, warto dla świętego spokoju zmienić. Jeżeli natomiast tylko chcemy “ukryć”, że nasza strona jest WordPressem to oczywiście nie warto bo jest wiele innych elementów, które determinują fakt, że nasza strona jest WordPressem i nie ma to najmniejszego sensu.

Dzisiaj pokażę sposoby na to, aby WordPress był bezpieczny od zaplecza, a jednocześnie żebyśmy nie musieli sobie utrudniać pracy z nim zmieniając domyślnych ścieżek. Moim zdaniem w większości przypadków nie ma zupełnie potrzeby zmieniania ścieżek.

Ochrona wp-admin na poziomie .htaccess i źródłowego IP

# Ochrona wp-admin

<IfModule mod_rewrite.c>

RewriteEngine on

RewriteCond %{REQUEST_URI} ^(.*)?wp-admin$

RewriteCond %{REMOTE_ADDR} !^111.111.111.111$

RewriteRule ^(.*)$ /not-found [R,L]

</IfModule>

Za pomocą prostego wpisu do pliku .htaccess możemy zabezpieczyć wp-admin’a na dostęp tylko z naszego IP, a wszystko co nie pasuje to naszego IP przekierujemy na inną podstronę. W tym przypadku przekierujemy wszystkich, którzy są z innego IP niż nasze na URL /not-found. Tutaj warto od razu utworzyć taką stronę w module strony w naszym zapleczu, aby przekierowywać wszystkich wścibskich na istniejącą stronę. Może to być także strona główna poprzez zmianę /not-found na sam slash /.

W ten sposób damy złudzenie atakującemu, że wp-admin nie jest dla niego. To w jaki sposób odbierze to odbiorca zależy od niego, ale efekt będzie na pewno identyczny jak zmiana nazwy wp-admin na coś innego. Podobnie atakujący zostanie przekierowany. Efekt dokładnie taki sam, a my nie musimy pamiętać o innym adresie. Jest to przydatne zwłaszcza, gdy mamy kilkadziesiąt stron w opiece i do każdego musielibyśmy pamiętać inne url’e.

Ochrona wp-login.php

No dobrze, zabezpieczyliśmy wp-admin ale aby dostać się do wordpressa, sam katalog wp-admin nie musi być znany i dostępny. Tak naprawdę /wp-admin kieruje nas do pliku wp-login.php, którym logujemy się do zaplecza. Jak go zabezpieczyć?

Moglibyśmy tak samo jak /wp-admin, ale. No właśnie, istnieje pewne “ale”. Co w sytuacji, kiedy mamy na stronie sklep na bazie najpopularniejszej wtyczki Woocommerce? Ten plugin wykorzystuje do zakładania kont i obsługi moduł użytkowników w WordPressie, więc całkowite odcięcie go sprawi, że żaden klient nie założy u nas konta i niczego nie kupi.

Jeżeli nie mamy sklepu Woocommerce wrzućmy poniższy wpis do .htaccess.

# Ochrona wp-login.php

<IfModule mod_rewrite.c>

RewriteEngine on

RewriteCond %{REQUEST_URI} ^(.*)?wp-login.php$

RewriteCond %{REMOTE_ADDR} !^111.111.111.111$

RewriteRule ^(.*)$ /not-found [R,L]

</IfModule>

Podobnie jak wyżej, przekierowujemy wszystkich spoza naszego IP na specjalnie przygotowaną stronę /not-found.

Jeżeli jednak mamy sklepik Woocommerce ten wpis uniemożliwi nam zbieranie i obsługę zamówień klientów. Wtedy skorzystajmy z innego wpisu, do pliku functions.php.

Nie dodawajmy wpisów do plików .php w samym core WordPressa, gdyż przy każdej aktualizacji i tak zostaną one nadpisane i ochrona przestanie działać. Zawsze dodawajmy takie wpisy do plików z motywem. Jeżeli motyw nie jest naszego autorstwa to oczywiście dodajemy wpisy do tak zwanego “child theme”, czyli motywu tworzonego na bazie oryginalnego, ale z tą różnicą, że żadne zmiany dopóki sami ich nie wprowadzimy nie zajdą w tych plikach.

Wpisy do pliku functions.php

add_action(’init’,’_restrict_wp_login’);

function _restrict_wp_login(){

            global $pagenow;

            $allowed = '111.111.111.111′; // Dozwolone IP

            // Pobierz IP uzytkownika

            if (!empty($_SERVER[’HTTP_CLIENT_IP’])) $ip = $_SERVER[’HTTP_CLIENT_IP’];

            elseif (!empty($_SERVER[’HTTP_X_FORWARDED_FOR’])) $ip = $_SERVER[’HTTP_X_FORWARDED_FOR’];

            else $ip = $_SERVER[’REMOTE_ADDR’];

            // Jesli z uzytkownik ma dozwolone IP to zwroc i pozwol

            if ( is_user_logged_in() || strpos($allowed, $ip ) !== false ) return;

            // Przekieruj do wyjscia poza przyciskiem w sklepie wyloguj

            if( 'wp-login.php’ == $pagenow && $_GET[’action’] != „logout”) {

            wp_redirect( home_url ( '/not-found’ ) );

            exit();

            }

}

Po wprowadzeniu powyższych ustawień do pliku functions.php nadal możemy obsługiwać zamówienia w Woocommerce, ale jednocześnie zabezpieczamy plik przed nieodpowiednim wykorzystaniem.

Powyższe regułki skutecznie zablokują dostęp do zaplecza osobom niepowołanym, jednocześnie pozostawiając sklepik – jeśli go mamy – w pełni funkcjonalnym. Do tego nie musimy zmieniać domyślnych ścieżek i pamiętać o nich. Wystarczy po prostu nasze IP. Co jeśli masz zmienne IP? Wszelkiego rodzaju tunele sesji, VPN’y będą idealne do tego typu operacji.

Nie samym zapleczem WordPress żyje człowiek

Zaplecze jest ważne, jednak nie tylko przez zaplecze wp-admin można dostać się do WordPressa i narobić szkód. Istnieje wiele endpointów, którymi roboty i crackerzy próbują dostać się do naszej strony. Zabezpieczymy najważniejsze z nich.

Zabezpieczenie xmlrcp.php

Jeżeli nie korzystasz z xmlrpc.php (w większości nikt nie korzysta) to spokojnie możemy ograniczyć do niego dostęp.

We wspomnianym już pliku functions.php naszego motywu potomnego lub naszego autorskiego, dodajmy wpisy blokujące wszelkie pingbacki do tego punktu:

function remove_xmlrpc_pingback_ping( $methods ) {

  unset($methods[’pingback.ping’]);

  unset($headers[’X-Pingback’]);

  return $methods;

}

add_filter(’xmlrpc_enabled’, '__return_false’);

add_filter(’xmlrpc_methods’, 'remove_xmlrpc_pingback_ping’ );

Dodatkowo konieczne będzie wrzucenie do pliku .htaccess dodatkowych regułek, aby zabezpieczyć w pełni dostęp:

# Blokada xmlrpc.php

<Files xmlrpc.php>

order deny,allow

deny from all

</Files>

Zabezpieczenie readme.html

W pliku readme są domyślne informacje, które mogą podpowiedzieć atakującemu jaką wersją WordPressa dysponujemy. Warto ukryć ten plik. Można go także usunąć, ale przy najbliższej aktualizacji i tak się pojawi, więc po prostu go zabezpieczmy wpisem do .htaccess.

# Ukryj readme

<FilesMatch „readme\.html”>

  Order allow,deny

  Deny from all

</FilesMatch>

Blokada enumerowania użytkowników

Wścibskie osoby i roboty atakujące często próbują wykonać tak zwaną enumerację użytkowników poprzez dodawanie kolejnych numerków. W ten sposób wykrywają ilu i jakich użytkowników ma nasza strona, a potem kolejno atakują ich hasła. Warto zablokować możliwość odwoływania się w ten sposób do końcówki z numerem id użytkownika. Wrzućmy poniższą regułkę do naszego .htaccessa.

# Blokada enumerowania uzytkownikow

RewriteCond %{QUERY_STRING} ^author=([0-9]*)

RewriteRule ^ /? [L,R=301]

Blokada pliku wp-config.php

Generalnie plik ten jako .php nie powinien być dostępny do pobrania, ale warto go i tak zabezpieczyć. Znajdują się tam dane dostępowe do naszej bazy danych. Wpiszmy zatem do .htaccess.

# Ochrona wp-config

<files wp-config.php>

order allow,deny

deny from all

</files>

Ochrona systemu komentarzy

O sile komentarzy i rozwoju dyskusji, dzięki której nasza strona staje się interaktywna nie trzeba nikomu mówić. Warto jednak zabezpieczyć system komentarzy przed możliwością zdalnego POST’owania komentarzy przez szkodników.

# Ochrona komentarzy

RewriteCond %{REQUEST_METHOD} POST

RewriteCond %{REQUEST_URI} .wp-comments-post\.php*

RewriteCond %{HTTP_REFERER} !.*mojastrona.pl.* [OR]

RewriteCond %{HTTP_USER_AGENT} ^$

RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]

Podmień “mojastrona.pl” na adres swojej strony internetowej. Dzięki temu tylko komentarze, które zostaną umieszczone poprzez interakcję i wejście na stronę będą akceptowane.

Ochrona pliku .htaccess

Często w pliku .htaccess zawieramy informację o ścieżkach (np. ścieżka do haseł przy ochronie katalogów na hasło). Nigdy taki plik nie powinien być publiczny i większość firm hostingowych na poziomie konfiguracji serwera odbiera do nich dostęp, ale nie zawsze tak jest, zatem zablokujmy go jeśli nie zrobił tego nasz dostawca hostingu.

# Ochrona .htaccess

<files ~ “^.*.([Hh][Tt][Aa])”>

order allow,deny

deny from all

satisfy all

</files>

Ochrona hotlinkowania

Jeżeli mamy jakieś materiały multimedialne na naszej stronie i nie chcemy, aby ktoś inny podlinkował się do nich i zjadał nasz transfer lub dodatkowo obciążał naszą stronę, warto pozbyć się hotlinkerów.

# Ochrona hotlinkowania

RewriteEngine on

RewriteCond %{HTTP_REFERER} !^$

RewriteCond %{HTTP_REFERER} !^http(s)?://(www.)?mojastrona.pl [NC]

RewriteRule .(jpg|jpeg|png|gif)$ – [NC,F,L]

W ten sposób powyższe pliki jak .jpg, .png czy .gif nie będą mogły być wyświetlone nigdzie poza naszą stroną. Możemy dodać kolejne rozszerzenia do ostatniej linijki na pliki filmowe i inne rozszerzenia oraz musimy zmienić mojastrona.pl na adres naszej strony www.

Własne strony błędów

Jeżeli nie chcemy korzystać z błędów dostarczonych przez WordPressa lub dostawcę hostingu możemy utworzyć w WordPressie dedykowane strony z błędami, dzięki czemu pokażemy użytkownikowi podczas błędu to co chcemy.

# Wlasne bledy

ErrorDocument 403 /access-denied

ErrorDocument 404 /not-found

ErrorDocument 500 /bad-request

Usuwanie wersji WordPressa z tagów

WordPress domyślnie zainstalowany wyświetla swoją wersję w kodzie strony. To daje atakującemu informacje o tym, z jakiej wersji korzystamy i może on dobrać pod tą wersję exploity, a tego nie chcemy. Ukryjmy zatem wersję WordPressa za pomocą wpisu do naszego pliku functions.php.

remove_action(’wp_head’, 'wp_generator’);

define(’SKILLHOUSE_FILE_VERSION’, '0000001′);

function rm_generator_filter() {

  return ”;

}

if(!function_exists(’skillhouse_remove_wp_ver_css_js’)) :

  function skillhouse_remove_wp_ver_css_js($src) {

            if(strpos($src, 'ver=’ . get_bloginfo( 'version’))) {

            $src = remove_query_arg( 'ver’, $src );

            }

            if(!strpos($src, ’?’)) {

            $src .= ’?ver=’ . SKILLHOUSE_FILE_VERSION;

            }

            return $src;

  }

endif;

Dodatkowe wpisy do functions.php ukrywające wszelkiego rodzaju informacje z “generatora”, które pojawiają się po instalacji WordPressa domyślnie w kodzie.

add_filter(’the_generator’, 'rm_generator_filter’);

add_filter(’style_loader_src’, 'skillhouse_remove_wp_ver_css_js’, 9999);

add_filter(’script_loader_src’, 'skillhouse_remove_wp_ver_css_js’, 9999);

add_filter(’get_the_generator_html’, 'rm_generator_filter’);

add_filter(’get_the_generator_xhtml’, 'rm_generator_filter’);

add_filter(’get_the_generator_atom’, 'rm_generator_filter’);

add_filter(’get_the_generator_rss2′, 'rm_generator_filter’);

add_filter(’get_the_generator_comment’, 'rm_generator_filter’);

add_filter(’get_the_generator_export’, 'rm_generator_filter’);

add_filter(’wf_disable_generator_tags’, 'rm_generator_filter’);

add_filter(’the_generator’, 'wpbeginner_remove_version’);

add_filter(’json_enabled’, '__return_false’);

add_filter(’json_jsonp_enabled’, '__return_false’);

Wyłącz edycję plików .php w WordPressie

Z reguły nie jest to potrzebne, z reguły też nie ma potrzeby korzystania z tego, ponieważ mało kto koduje w języku .php w WordPressie, a jeszcze mniej tych, którzy to robią korzysta z domyślnego edytora wbudowanego w aplikację. Warto wyłączyć ten edytor.

Za pomocą wpisu do pliku wp-config.php wyłączymy edytor na stałe.

define( 'DISALLOW_FILE_EDIT’, true );

Wyłącz DEBUG

Z reguły nie ma potrzeby wyświetlania błędów aplikacji osobom odwiedzającym naszą stronę, a już na pewno robotom, które będą testować podatności na naszej stronie. Takie informacje dla nich są bardzo cenne i mogą spowodować nakierowanie wektoru ataku na dane błędy. Wyłączmy je na stałe za pomocą prostego wpisu do wp-config.php.

define( 'WP_DEBUG’, false);

if ( ! WP_DEBUG ) {

  ini_set(’display_errors’, 0);

}

Wyłącz wykonywanie kodu .php w katalogach do tego nie przeznaczonych

W katalogach, w których na przykład ładujemy pliki multimedialne, wykonywanie kodu .php powinno być zabronione, gdyż atakujący, który uzyska dostęp mógłby wrzucić tam i wykonać złośliwy kod. Takim miejscem jest na pewno /wp-content/uploads. Zabezpieczmy go poprzez wrzucenie do tego folderu osobnego pliku .htaccess z poniższym wpisem.

<Files *.php>

deny from all

</Files>

Dzięki temu, żaden plik .php nie wykona się w tym miejscu.

Dwuskładnikowe uwierzytelnianie

Podczas logowania do zaplecza WordPressa wystarczy podanie loginu i hasła. Warto skorzystać z oficjalnego pluginu, który pomoże nam w podniesieniu bezpieczeństwa tego procesu i logowanie się dwuskładnikowo.

Dzięki temu, podczas każdego logowania po wprowadzeniu loginu i hasła, system przekieruje nas do drugiego składnika uwierzytelniania, gdzie konieczne będzie podanie kodu z aplikacji w smartfonie. Wygląda to podobnie jak logowanie z tokenem do bankowości internetowej.

Najlepszym sposobem na zrobienie dwuetapowego logowania jest zainstalowanie pluginu Authy wraz z aplikacją na smartfona lub autentykacji od Google’a.

Po zainstalowaniu wtyczki w WordPressie i aplikacji na smartfonie, proces łączenia naszego WordPressa z aplikacją odbywa się za pomocą zeskanowania QR Code.Zmiana prefixów w bazie danych

Niektórzy zalecają zmianę domyślnego prefixu wp_ na coś innego, jednak w sytuacji, kiedy atakujący dostanie się do naszej bazy danych to bez różnicy będzie dla niego to jaki mamy prefix. Dostęp do danych będzie miał tak czy siak. Jeżeli jesteśmy jednak paranoikami i chcemy mieć większe poczucie bezpieczeństwa, niż realne zagrożenie możemy zmienić prefix tabel zmieniając je bezpośrednio w bazie i aktualizując wpisy w wp-config.php.

Wyłączenie indeksowania zawartości katalogu

Domyślne ustawienie zależy od administratora naszego hostingu i jest 50/50. Część firm wyłącza globalnie indeksowanie, część firm włącza je celowo, żeby użytkownicy, którzy chcą mieć indeksowanie nie zawracali głowy supportowi, a ci bardziej obeznani i tak je wyłączą.

Aby wyłączyć indeksowanie w katalogu WordPressa, dodajmy wpis do naszego głównego pliku. htaccess.

Options -Indexes

Co poza WordPressem jeszcze da się zrobić?

Aktualna wersja PHP

Zabezpieczenie zaplecza, plików i katalogów to jedno. Niemniej ważną kwestią jest to gdzie tego WordPressa trzymamy ma znaczenie. Warto zwrócić uwagę na to z jakiej wersji PHP korzystamy. Najnowszy WordPress współpracuje doskonale z najnowszymi wersjami bibliotek PHP więc warto przesiąść się na najnowsze, wspierane i aktualizowane przez twórców.

Na stronie twórców PHP można podejrzeć ilustrację obrazującą jak poszczególne wersje PHP są wspierane.

https://www.php.net/supported-versions.php

Wynika z tej tabeli jasno, że nie powinniśmy stawiać stron na wersji PHP niższej niż 7.2 przy czym wersja 7.2 otrzymuje już tylko poprawki bezpieczeństwa. Sugeruję mimo wszystko korzystanie z minimum wersji 7.3.

Tylko po SSL’u

Jak już wspomniałem w poprzedniej części artykułu strona powinna być wystawiona wyłącznie po bezpiecznym protokole HTTPS wraz z ważnym certyfikatem SSL. Pamiętajmy o tym, chodzi przecież o nasze i nie tylko nasze dane osobowe.

Dodatkowo możemy wymusić dla pewności, aby zaplecze było zawsze dostępne tylko po SSL poprzez poniższy wpis do pliku wp-config.php

define(’FORCE_SSL_ADMIN’, true);

Podsumowanie

Zbiór powyższych reguł oraz wpisów powinien skutecznie zabezpieczyć naszego WordPressa przed robotami jak i wścibskimi osobami, które chcą dostać się do naszej strony i jej zaplecza. Nie wyczerpuje on z pewnością wszystkiego co da się zrobić, ale jest solidną bazą i powinien mieć zastosowane na każdej stronie zasilanej tym wyjątkowo dobrze rozbudowanym i funkcjonalnym CMS’em jakim jest WordPress.