Dzisiejszy dzień zaczął się dla mnie dość dziwnie. Poranne spotkanie z klientem skończyło się przed czasem z powodu nie działania serwisu testowego! Co dziwne, czasem coś się pojawiało, czasem nic. Generalnie dość nieprzyjemna sytuacja, biorąc pod uwagę, działo się to na prezentacji serwisu, krótko przed odbiorem.
A że serwis testowy jest jedną z witryn w sieci blogów, do której należy również ta witryna, to również ona nie działa.
Szybkie logowanie na serwer pozwala ustalić przyczynę.
Do pliku
wp-login.php
leciało i leci kilka zapytań na sekundę, jako spreparowane próby zalogowania się.
Dane oczywiście słownikowe typu:
administrator cheetah administrator older administrator sabres administrator some admin wings iworks beaches iworks greenbay iworks legacy iworks pothead iworks spice
Serwer www oczywiście bez kłopotu to przyjmował, ale niestety proces fcgi już nie dawał rady z obsługiwaniem takiej liczby zapytań.
Rozwiązanie jest bardzo proste:
Należy zmienić nazwę pliku wp-login.php na inną.
No taaaak.
Ja się zaloguję, ale co z klientami? Z idącym cały czas atakiem nie ma problemu, bo to nie jest próba logowania na adres, który otrzymujemy po próbie dostępu do zasobu wp-admin, tylko brutalne wysyłanie metodą POST.
Tutaj na pomocą przychodzi filtr site_url
W celu zapewnienia wyświetlania poprawnej strony po wylogowaniu skorzystać należy z filtra wp_redirect
Kod obsługujący:
class iWorks_WP_Login_Redirect { private $login_prefix = 'vuushiep-'; public function __construct() { add_filter( 'site_url', array( &$this, 'site_url' ), 10, 2 ); add_filter( 'wp_redirect', array( &$this, 'wp_redirect' ), 10, 2 ); } public function site_url( $url, $file ) { if ( 'wp-login.php' == $file ) { return preg_replace( '/wp-login/', $this->login_prefix.'wp-login', $url ); } return $url; } public function wp_redirect( $location, $status ) { if ( 'wp-login.php?loggedout=true' == $location ) { return preg_replace( '/wp-login/', $this->login_prefix.'wp-login', $location ); } return $location; } } new iWorks_WP_Login_Redirect(); |
Cały kod mikro-wtyczki możesz pobrać i używać do woli.
Bardzo ważne!
Zmień nazwę pliku wp-login.php na taki który będzie zgodny z tym co używa załączony kod, tutaj vuushiep-wp-login.php
Łukasz zwrócił uwagę na dość istotną kwestię. Jeżeli nie macie CGI, tylko standardowy serwer www, to zawartość pliku
wp-login.php
wymienić na następujący kod:
<?php header("HTTP/1.0 404 Not Found"); |
Aktualizacja 2013-09-25 20:30
Można dodać do serwera regułę banującą dostęp do pliku wp-login.php w celu uniknięcia dostępu do niego po aktualizacji i co za tym idzie odtworzeniu pliku.
Konfiguracja dla nginxa:
location ~ ^/wp-login\.php { deny all; } |
konfiguracja dla apache’a:
<files wp-login.php> deny from all </files> |
Aktualizacja: 2014-11-21
Ja u siebie (nginx) nie kasuję pliku wp-login.php a tylko tworzę do niego symlinka. W rezultacie plik zawsze jest aktualny. Dostęp do niego jest zakazany w konfiguracji wirtuala (nginx).
Aktualizacja: 2016-09-21
Zamiast kasować/zmieniać nazwę pliku wp-login.php
lepiej jest zrobić symlinka do pliki wp-login.php oraz zablokować sam plik wp-login.php w konfiguracji wirtuala lub za pomocą pliku .htaccess.
Po zalogowaniu się i przejściu do odpowiedniego folderu:
ln -s wp-login.php vuushiep-wp-login.php
Jak użyć podany kod?
Masz 3 rozwiązania:
- wrzuć załączony plik do folderu
wp-content/plugins
, a potem włącz wtyczkę - wrzuć załączony plik do folderu
wp-content/mu-plugins
- dołącz kod do pliku
functions.php
aktualnie używanego motywu
Łukasz - Majsterkowo.pl
Problem tylko taki, że wp-login.php przywróci się po aktualizacji WP…
Poza tym – WP nie będzie serwował swojej 404 przy próbie wejścia na nieistniejący wp-login.php?
Marcin Pietrzak
@Łukasz: faktycznie dla instalacji na apache’u będzie taka sytuacja jak przedstawiasz i wtedy warto zawartość pliku wp-login.php zmienić na header(„HTTP/1.0 404 Not Found”, dopiszę to.
Jenny
„iworks beaches” <3 (chociaż w wersji audio brzmi lepiej ;))
DMati
Nie lepiej zamiast tykać wp pliki ukryć url do wp-login.php?
W efekcie zamiast:
domena.pl/wp-login.php => np: domena.pl/kokpit
Marcin Pietrzak
@DMati: nie rozumiem tego co sugerujesz, czy możesz rozwinąć?
Paweł
@Marcin, zostaje jeszcze kwestia wspomnianych aktualizacji, które skutecznie rozkładają całość przywracając oryginalny wp-login, no i konieczność ręcznej zabawy plikami…
Aby to było funkcjonalne jako wtyczka, wypadało by nieco rozwinąć -automatyzując operacje plikowe- mam nadzieję, że rozumiesz w czym rzecz. ;)
-pewną podpowiedź do tego typu rozwinięcia może stanowić taki najprostszy przykładzik pastebin.com/P7CBttfH
Daggerka
Nie łatwiej wyciąć dostęp do pliku przez dodatkową autoryzacje (na apache / lighttpd / nginx)? Klient będzie musiał (raz na jakiś czas) wpisać swój login i hasło dwukrotnie.
Oczywiście boty pewnie będą dalej próbowały się dobijać, ale obciążenie serwera powinno spaść niemal do zera.
Marcin Pietrzak
@Paweł: śmiało, kod jest na GPL można pobierać i modyfikować. :D Ale! u mnie serwer www nie ma prawa zapisu w katalogach oprócz /wp-content/files (instalacja MU).
Kod aktualizuję bezpośrednio z subversion (svn switch) co dodatkowo ma kilka zalet i wad, ale generalnie będę widział pliki, szczególnie że svn zakrzyczy że plik wp-login.php znikł trzeba go odtworzyć.
Zawsze można dać w konfiguracji virtuala bana na /wp-login.php, więc kwestię pojawianie się pliku uważam za ogarniętą.
@Daggerka: można, ale podstawowe logowanie dla niektórych klientów jest problemem, podwójne będzie dla nich nie do ogarnięcia.
—
Chyba najlepszym rozwiązaniem jest zbanowanie tego pliku w konfiguracji vhosta.
Maciej
Jeszcze pojawia się kwestia odzyskiwania hasła.
Po zmianie nazwy pliku wp-login.php na własną można się logować, natomiast po próbie odzyskiwania hasła (wpisanie nazwy użytkownika i akceptacja) przekierowuje na domyślny plik wp-login.php
Marcin Pietrzak
@Maciej: faktycznie tak jest, już zacząłem pisać dalej (tutaj działa odzyskiwanie hasła), ale sprawa okazała się bardzo pracochłonna, bo jest bardzo dużo akcji związanych z logowaniem. Dodatkowo okazało się, że to co już zrobiłem nie działa dla WordPressa stojącego jako pojedyncza strona (ten tutaj to multisite) i jak testowałem lokalnie wyszło, że odzyskiwanie hasła nadal nie działa.
Jak tylko znajdę troszkę czasu, postaram się całość opanować i udostępnić.
Maciej Budzisz
Najważniejsze, że już jakaś podstawa jest, która zabezpiecza przed atakiem.
U mnie zaczęło się od jednej strony, a później kolejne zaczęły być atakowane. Klienci się rzadko sami logują do panelu, więc na razie powinno to wystarczyć.
To i tak duża pomoc z Twojej strony, za którą dziękuję. Czekam również na zmodernizowane rozwiązanie ;)
Artur Jaskólski
Zgadzam się z DMati. Najprostszym rozwiązaniem będzie dodanie do pliku .htaccess nastepującej reguły:
RewriteRule ^/kokpit/?$ /wp-login.php [QSA,L]
To powinno załatwić sprawę.
Łukasz
Metoda bardzo dobrze działa dla wp-login.php, ale w momencie wejścia przez końcówkę /admin lub /login od razu przenosi nas do symlinka i odkrywa go w pasku adresu – czyli /de facto bardzo łatwo odkryć jaki jest nowy adres pliku php związanego z logowaniem i go atakować.
Marcin Pietrzak
Łukasz, masz 100% racji. Wejście na /wp-admin/ też odkrywa nowy adres. Cała metoda nie ma na celu „całkowitego ukrycia” (co jest oczywiście możliwe), tylko na mocne ograniczenie automatycznych ataków. Atakujący nie sprawdzają odpowiedzi, nie podążają za przekierowaniami i nie atakują niestandardowych adres. Jeżeli tak prosta metoda może ograniczyć 99% ataków, to i tak IMVHO warto ją zastosować – u mnie się sprawdziło.