WordPress: Ukrycie wp-login.php oraz wp-admin/

Spis treści

By dokonywać jakichkolwiek zmian w WordPressie (i mowa tu nie tylko o dodawaniu treści ale także zmianie jego plików źródłowych), to musimy uzyskać dostęp do panelu administracyjnego, a tu z kolei możemy się dostać tylko za pośrednictwem formularza logowania. Panel admina możemy wywołać przez dopisanie do adresu strony wp-admin/ , z tym, że jeśli nie jesteśmy zalogowani, to automatycznie zostaniemy przekierowani na wp-login.php . Nie oznacza to bynajmniej, że nie możemy wykonać zapytania do plików znajdujących się w katalogu wp-admin/ . Obie te powyższe lokalizacje muszą być traktowane ze szczególną ostrożnością, a dostęp do nich limitowany i bardzo restrykcyjny.

Dodatkowe wtyczki

Możemy się spotkać z szeregiem pluginów, które w mniejszym lub większym stopniu próbują adresować ten problem ale czy jest sens zaciągać do pracy dodatkowy kod, gdy możemy w bardzo prosty sposób limitować dostęp do tych krytycznych miejsc w serwisie? W tym artykule są opisane z grubsza dwa wektory ataku na skrypt WordPressa. Pierwszym są podatności samego kodu tego CMSa, natomiast drugi z nich dotyczy ataku Brute Force z wykorzystaniem formularza logowania w celu złamania hasła do konta administratora.

Pliki w wp-admin/ oraz skrypt wp-login.php

Jak już wspomniałem wyżej, osobie niezalogowanej, która przegląda naszego bloga, zostanie przedstawiony formularz logowania, gdy ta odwiedzi wp-admin/ lub wp-login.php . Jeśli prowadzimy serwis samotnie i mamy przy tym zaimplementowany zewnętrzny system komentarzy, np. disqus, to mamy bardzo uproszczone zadanie, bo ochrona naszej strony sprowadzać będzie się do wskazania serwerowi komu ma wyświetlać formularz logowania i panel admina.

Analiza zapytania

Zamieńmy się na chwilę w internetowego bota i odwiedźmy naszą stronę próbując wyciągnąć jakiś plik z katalogu wp-admin/ . W tym celu posłużymy się narzędziem wget , przykładowo:

$ wget --spider https://morfitronik.lh/wp-admin/about.php --no-check-certificate

Poniżej przeanalizujemy sobie log z tego co się tak naprawdę stanie po wykonaniu powyższego polecenia. Opcja --no-check-certificate jest dodana by obejść problemy z własnoręcznie wygenerowanym certyfikatem i bez niej nie dałoby rady wykonać zapytania. Z kolei parametr --spider sprawi, że wget będzie zachowywał się bardziej jak zwykły bot (coś na wzór googlowskiego) i nie pobierze żadnej części witryny, jedynie zwróci szereg komunikatów.

Po wklepaniu powyższego polecenia w terminalu, najsampierw jest rozwiązywana nazwa hosta morfitronik.lh na adres IP, po czym pod ten adres jest wysyłane żądanie:

...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:10--  https://morfitronik.lh/wp-admin/about.php
Resolving morfitronik.lh (morfitronik.lh)... 192.168.10.10
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:80... connected.
...

Jako, że ja u siebie w środowisku testowym mam wymuszone korzystanie z protokołu SSL, to chwilę po wysłaniu zapytania, zostanie zwrócony komunikat, że zasób został przeniesiony permanentnie (kod błędu 301 ) na inny adres:

...
HTTP request sent, awaiting response... 301 Moved Permanently
Location: https://morfitronik.lh/wp-admin/about.php [following]
...

Po czym zapytanie zostanie przetworzone jeszcze raz, z tym, że w oparciu o nowy URL:

...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:10--  https://morfitronik.lh/wp-admin/about.php
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:443... connected.
WARNING: The certificate of 'morfitronik.lh' is not trusted.
WARNING: The certificate of 'morfitronik.lh' hasn't got a known issuer.
The certificate's owner does not match hostname 'morfitronik.lh'
...

Po wysłaniu zapytania, klient czeka na odpowiedź z serwera i po chwili ją uzyskuje, ale jako że ten bot nie jest zalogowany w systemie, zamiast żądanego pliku zostanie przekierowany na wp-login.php:

...
HTTP request sent, awaiting response... 302 Found
Location: https://morfitronik.lh/wp-login.php?redirect_to=https%3A%2F%2Fmorfitronik.lh%2Fwp-admin%2Fabout.php&reauth=1 [following]
...

Po przekierowaniu, zostaje przetworzone kolejne zapytanie, tym razem w oparciu o skrypt logowania wp-login.php :

...
Spider mode enabled. Check if remote file exists.
--2015-05-31 15:27:11--  https://morfitronik.lh/wp-login.php?redirect_to=https%3A%2F%2Fmorfitronik.lh%2Fwp-admin%2Fabout.php&reauth=1
Connecting to morfitronik.lh (morfitronik.lh)|192.168.10.10|:443... connected.
WARNING: The certificate of 'morfitronik.lh' is not trusted.
WARNING: The certificate of 'morfitronik.lh' hasn't got a known issuer.
The certificate's owner does not match hostname 'morfitronik.lh'
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Remote file exists and could contain further links,
but recursion is disabled -- not retrieving.

Kod błędu 200 oznacza, że zasób istnieje i został zwrócony do bota. Serwer nie odmówił dostępu do pliku wp-admin/about.php . Zamiast tego przekierował nas na stronę logowania i tutaj również serwer nie dorzucił żadnych obostrzeń ze swojej strony.

Dla porównania, jeśli byśmy spróbowali uzyskać dostęp do nieistniejącego pliku w katalogu wp-admin/ , to zostanie mam zwrócony poniższy log:

$ wget --spider  https://morfitronik.lh/wp-admin/about.php-test --no-check-certificate
...
HTTP request sent, awaiting response... 404 Not Found
Remote file does not exist -- broken link!!!

I tu już mamy kod błędu 404 i właśnie z tego powodu, powinniśmy zablokować możliwość wejścia w katalog wp-admin/ nieuprawnionym osobnikom.

Trzeba jednak pamiętać, że niektóre pluginy czy motywy wymagają do poprawnego działania pewnych plików z katalogu wp-admin/ , np. admin-ajax.php .

Na tym blogu tylko ja będę miał możliwość korzystania z logowania do panelu WordPressa, no bo wszyscy ludzie, którzy będą chcieli komentować wpisy, będą to już robić przy pomocy disqus'a, a tam już jest osobny mechanizm logowania i kompletnie niepowiązany ze tym WordPressowskim. Dzięki czemu nie muszę utrzymywać i martwić się o bazę danych użytkowników, oraz jeśli ktoś posiada konto na disqus'ie, to będzie miał możliwość komentowania nie tylko moich treści ale również i kontentu całej masy innych blogerów.

Zakładanie blokady na wp-login.php i wp-admin/

Mamy kilka opcji co do wprowadzenia restrykcji na te dwa zasoby. Pierwszą z nich jest moduł Apache mod_rewrite , przy pomocy którego to przepiszemy odpowiednie adresy chowając w ten sposób te dwie lokalizacje. Drugi sposób zaś wykorzystuje dyrektywy Files oraz MatchFiles do odmawiania dostępu do plików w obrębie instalacji WordPressa.

Moduł mod_rewrite

Jeśli chcemy skorzystać z modułu mod_rewrite , to w konfiguracji serwera Apache dla katalogu z instalacją WordPressa lub w pliku .htaccess dopisujemy poniższy kod:

<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_URI} wp-login.php|wp-admin
    RewriteCond %{REMOTE_ADDR} !^192.168.10.100$
    RewriteRule . - [R=403,L]
</IfModule>

Mamy tam zdefiniowane dwa warunki i oba z nich muszą być spełnione by serwer zwrócił kod błędu 403 i zakończył przetwarzanie zapytania. Pierwszym z nich jest wywoływanie adresów mających w nazwie wp-login.php lub wp-admin . Drugim zaś adres IP i w tym przypadku każdy adres, za wyjątkiem 192.168.10.100 będzie pasował.

Dyrektywy Files i MatchFiles

Jeśli nie chcemy korzystać z powyższego rozwiązania, możemy zaprzęgnąć do roboty dyrektywę Files lub MatchFiles . Nie różnią się one zbytnio, jednak ten drugi powinien być używany w kontekście szerszego dopasowania plików, np. za pomocą wyrażeń regularnych. Zatem jeśli chcemy przy pomocy tych dyrektyw zablokować dostęp do panelu admina i formularza logowania, dopisujemy w konfiguracji serwera albo również w pliku .htaccess poniższe zwrotki:

<Files wp-login.php>
    Require all denied
    Require ip 192.168.10.100
</Files>
<Files wp-admin>
    Require all denied
    Require ip 192.168.10.100
</Files>

Test zabezpieczeń

Odpalmy zatem jeszcze raz wget i sprawdźmy czy jest zwracany ten komunikat co trzeba:

$ wget --spider  https://morfitronik.lh/wp-admin/about.php --no-check-certificate
...
HTTP request sent, awaiting response... 403 Forbidden
Remote file does not exist -- broken link!!!

Zatem blokada zadziałała. Istnieje możliwość przestawienia kodu błędu na 404 ale nie zalecam tego robić, bo niekoniecznie googlowi może się podobać, że w naszym serwisie są linki do nieistniejących zasobów. Od tej pory jedynie klienci z adresu IP 192.168.10.100 będą mieć dostęp do panelu admina i formularzu logowania.

Mikhail Morfikov avatar
Mikhail Morfikov
Po ponad 10 latach spędzonych z różnej maści linux'ami (Debian/Ubuntu, OpenWRT, Android) mogę śmiało powiedzieć, że nie ma rzeczy niemożliwych i problemów, których nie da się rozwiązać. Jedną umiejętność, którą ludzki umysł musi posiąść, by wybrnąć nawet z tej najbardziej nieprzyjemniej sytuacji, to zdolność logicznego rozumowania.