secaudit.eu
Zabezpieczenia

Enumeracja użytkowników WordPress: REST API i URL autorów

Enumeracja użytkowników WordPress: jak sprawdzić, które loginy ujawniają REST API, adresy autorów, mapa witryny i błędy logowania, oraz jak bezpiecznie to zamknąć.

Autorka: , AngelForce OÜ 6 min czytania

Także po: English Deutsch Français Español Italiano Português Türkçe

Na domyślnej instalacji WordPressa loginy użytkowników zwykle da się ustalić w niecałą minutę. Enumeracja użytkowników WordPress działa przez pięć, sześć wbudowanych kanałów: REST API, przekierowania ?author=1, adresy archiwów autorów, mapę witryny z użytkownikami, komunikaty błędów logowania, a czasem także kanały RSS i dane oEmbed. Poniżej pokazujemy, jak przetestować każdy z nich na własnej stronie i jak go zamknąć wąską poprawką, która nie psuje edytora blokowego.

Czym jest enumeracja użytkowników WordPress i jak bardzo ma znaczenie

Enumeracja użytkowników to zbieranie prawidłowych nazw kont z danej witryny. Atakujący, który wie, że istnieje konto mkeller, może wykorzystać każdą próbę hasła i każde hasło z innych wycieków na prawdziwym koncie, zamiast zgadywać admin, info i test.

Trzeba jednak trzeźwo ocenić, co daje ukrywanie nazw: to utrudnienie, a nie ochrona. Przejęcie konta zatrzymują uwierzytelnianie dwuskładnikowe i ograniczanie liczby prób logowania, więc najpierw należy wdrożyć je (uwierzytelnianie dwuskładnikowe w WordPressie, ochrona przed atakami brute force). Dopiero potem zamykamy opisane niżej wycieki, w kolejności: test, poprawka, ponowny test.

Diagram: pięć kanałów, którymi wyciekają loginy WordPress, każdy z poprawką
Którędy wyciekają loginy i jaka jest wąska poprawka dla każdego kanału

Test własnej strony: sześć miejsc, w których wyciekają loginy

Testy wolno wykonywać wyłącznie na witrynach, które są Państwa własnością lub które Państwo administrują. Należy testować jako użytkownik niezalogowany, z terminala lub w oknie prywatnym przeglądarki, ponieważ sesje zalogowane widzą więcej. Zamiast example.com wstawiamy własną domenę.

  1. REST API. Rdzeń WordPressa wylistuje użytkowników, którzy opublikowali treści. Warto spojrzeć na pole slug, czyli slug autora, zwykle identyczny z loginem.
curl -s https://example.com/wp-json/wp/v2/users | grep -o '"slug":"[^"]*"'
curl -s "https://example.com/?rest_route=/wp/v2/users" | grep -o '"slug":"[^"]*"'
  1. Przekierowania po ID autora. WordPress przekierowuje ?author=N na „ładny” adres autora. Należy spróbować z 1, 2, 3 i kolejnymi numerami.
curl -sI "https://example.com/?author=1" | grep -i '^location'
  1. HTML motywu. Linki do autorów przy wpisach oraz klasy CSS w rodzaju comment-author-<slug> przy komentarzach zarejestrowanych użytkowników.
curl -s https://example.com/a-post/ | grep -oE '/author/[^/"]+/|comment-author-[a-z0-9-]+' | sort -u
  1. Mapa witryny z użytkownikami. Należy otworzyć w przeglądarce https://example.com/wp-sitemap-users-1.xml.
  1. Błędy logowania. Na wp-login.php wpisujemy wymyśloną nazwę użytkownika, a potem prawdziwą z błędnym hasłem. Jeśli komunikaty się różnią, formularz potwierdza istnienie kont. Formularz odzyskiwania hasła zachowuje się tak samo. Wystarczą dwie lub trzy próby, żeby własne ograniczenia nie zablokowały dostępu.
  1. Kanały RSS i oEmbed. Kanał RSS pokazuje nazwy wyświetlane w dc:creator. Endpoint oEmbed zwraca author_name i author_url, a ten adres zawiera slug autora.
curl -s https://example.com/feed/ | grep -i 'dc:creator'
curl -s "https://example.com/wp-json/oembed/1.0/embed?url=https://example.com/a-post/"
KanałTestCo ujawnia
REST users/wp-json/wp/v2/usersSlug, nazwę, ID
Przekierowanie autora/?author=1Slug w nagłówku Location
HTML motywuZnaczniki wpisów i komentarzySlug
Mapa witryny użytkowników/wp-sitemap-users-1.xmlAdresy autorów ze slugiem
Błędy logowaniaNieznany login a błędne hasłoPrawidłowe loginy
RSS / oEmbeddc:creator, author_urlNazwę wyświetlaną, slug

Jeśli potrzebna jest uporządkowana metodyka, testowanie enumeracji kont opisuje dokładniej OWASP Web Security Testing Guide.

Ograniczenie endpointu REST z użytkownikami tylko dla anonimowych odwiedzających

Należy wyciąć wyłącznie trasę użytkowników i tylko dla żądań bez zalogowanego użytkownika. Edytor blokowy korzysta z /wp/v2/users w selektorze autora, a aplikacje mobilne i wiele wtyczek wywołuje ją z uwierzytelnieniem. Po usunięciu trasy dla wszystkich przestałyby działać.

Tworzymy plik wp-content/mu-plugins/user-enumeration.php. Wtyczki must-use ładują się automatycznie i nie da się ich wyłączyć z poziomu kokpitu. Filtr rest_endpoints pozwala usunąć trasy, zanim zostaną obsłużone:

<?php
/**
 * Plugin Name: Limit user enumeration
 */

add_filter( 'rest_endpoints', function ( $endpoints ) {
    if ( is_user_logged_in() ) {
        return $endpoints;
    }
    unset( $endpoints['/wp/v2/users'] );
    unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    return $endpoints;
} );

Należy ponownie uruchomić test z curl. Teraz powinien pojawić się błąd rest_no_route ze statusem 404. Następnie trzeba się zalogować, otworzyć wpis w edytorze i sprawdzić, czy panel autora nadal wyświetla użytkowników.

Jeśli używają Państwo front-endu headless albo aplikacji, która anonimowo odczytuje dane autorów, ten fragment kodu ją zepsuje. Wtedy lepiej udostępnić tylko potrzebne pola przez własny endpoint. Co zwraca endpoint, wymienia dokumentacja użytkowników w REST API.

Zablokowanie przekierowań po ID autora i decyzja, czy archiwa autorów są potrzebne

Gdy ktoś zażąda /?author=2, WordPress ustala użytkownika, a kanoniczne przekierowanie kieruje odwiedzającego na /author/<slug>/. Nagłówek Location zdradza slug, a skrypt przejdzie ID od 1 do 50 w kilka sekund.

Poniższy kod dodajemy pod pierwszym fragmentem w tej samej wtyczce mu-plugin. Uruchamia się przed kanonicznym przekierowaniem i odpowiada anonimowym żądaniom z parametrem author zwykłym kodem 404, więc żaden nagłówek Location nie zdradza sluga:

add_action( 'template_redirect', function () {
    if ( is_user_logged_in() ) {
        return;
    }
    if ( isset( $_GET['author'] ) ) {
        wp_die( 'Not found.', '', array( 'response' => 404 ) );
    }
}, 1 );

Ponowny test: curl -sI "https://example.com/?author=1". Powinien zwrócić 404 i brak nagłówka Location.

Na firmowym blogu z jednym autorem albo w witrynie bez publicznych stron autorów można pójść dalej i wyłączyć archiwa autorów. Większość wtyczek SEO ma na to ustawienie, niektóre motywy również. Trzeba jednak uczciwie przyznać, że to ma swoje granice: jeśli motyw wyświetla przy każdym wpisie link „autor: …”, slug nadal jest w kodzie HTML. Tym zajmują się dwa kolejne rozdziały.

Usunięcie mapy witryny z użytkownikami tam, gdzie nikt jej nie potrzebuje

Mapy witryny w rdzeniu, dodane w WordPressie 5.5, zawierają dostawcę „users”, który wymienia każde archiwum autora. Niewiele firmowych stron na tym korzysta. Usuwa go filtr wp_sitemaps_add_provider:

add_filter( 'wp_sitemaps_add_provider', function ( $provider, $name ) {
    if ( 'users' === $name ) {
        return false;
    }
    return $provider;
}, 10, 2 );

Należy jeszcze raz otworzyć /wp-sitemap-users-1.xml; teraz powinien zwracać 404. Jeśli mapy witryny rdzenia zastępuje wtyczka SEO, ten filtr nie wpływa na jej wynik. W ustawieniach mapy witryny takiej wtyczki trzeba poszukać mapy autorów i wyłączyć ją tam.

Nazwy wyświetlane i slugi autorów inne niż loginy

W Użytkownicy > Profil ustawiamy Pseudonim i wybieramy go w polu Wyświetlaj publicznie jako. To obejmuje kanały RSS, author_name w oEmbed i wiersze „autor:”.

Jest tu jednak pułapka. Slug autora (user_nicename) powstaje z loginu przy zakładaniu konta i nie zmienia się wraz ze zmianą nazwy wyświetlanej. We wpisach może widnieć „Zespół redakcji”, a adres nadal brzmi /author/mkeller/. Naprawia to WP-CLI:

wp db export before-nicename.sql
wp user list --fields=ID,user_login,user_nicename,display_name
wp user update 2 --user_nicename=editorial-team

Należy używać małych liter, cyfr i myślników oraz dbać o unikalność każdego sluga. Dawne adresy autorów się zmienią, więc trzeba liczyć się z kilkoma błędami 404 albo dodać przekierowanie, jeśli te strony miały ruch.

Dla nowych kont lepiej unikać loginów admin, nazwy firmy i nazwy domeny. Jeśli częścią problemu są stare lub współdzielone konta, najpierw trzeba je uporządkować: bezpieczeństwo ról użytkowników w WordPressie.

Ogólne komunikaty błędów logowania

Filtr login_errors zastępuje komunikaty wyświetlane na ekranie logowania jednym, neutralnym tekstem:

add_filter( 'login_errors', function () {
    return '<strong>Error:</strong> The login details are incorrect.';
} );

Zastępuje to także komunikaty pomocne, na przykład podpowiedź o pustym polu, więc użytkownicy dostają mniej wskazówek. Nie czyni to również witryny nieprzejrzystą. Formularz odzyskiwania hasła nadal zachowuje się inaczej dla prawidłowych kont, bo prawdziwe konto prowadzi do ekranu „sprawdź pocztę”. Niektóre wtyczki dodają własne formularze logowania, a strona Moje konto w WooCommerce ma własne komunikaty. Warto traktować to jako kwestię spójności, nie jako mocne zabezpieczenie.

Czego ukrywanie loginów nie powstrzyma

Atakujący próbują popularnych nazw i adresów e-mail niezależnie od tego, czy strona je ujawnia, a WordPress akceptuje adres e-mail jako login. Credential stuffing, czyli odtwarzanie par login–hasło z innych wycieków, w ogóle nie wymaga enumeracji. Idealnie ukryta lista loginów nic nie da przy powtórnie użytym haśle.

Kolejność działań dla witryny małej firmy wygląda więc tak:

  1. Uwierzytelnianie dwuskładnikowe dla każdego administratora i redaktora (instrukcja wdrożenia).
  2. Ograniczanie prób logowania i decyzja w sprawie XML-RPC (ochrona przed atakami brute force).
  3. Opisane wyżej poprawki przeciw enumeracji, sprawdzone poleceniami curl.
  4. Włączenie wyniku do regularnego przeglądu (lista kontrolna bezpieczeństwa WordPressa).

Wtyczka Security Audit & Compliance od secaudit.eu zawiera ograniczanie prób logowania i limity zapytań anonimowych do REST API, a jej skan utwardzania zgłasza ustalenia dotyczące konfiguracji, dzięki czemu widać, co nadal jest otwarte → secaudit.eu

Następny krok: jeszcze dziś wykonać na własnej stronie sześć testów z tego artykułu, zapisać wyniki i naprawić kanały, w których widać prawdziwy login.

FAQ

Czy blokada /wp/v2/users psuje edytor blokowy?

Nie, jeśli ogranicza się ją do żądań anonimowych, jak w powyższym fragmencie kodu. Edytor wysyła żądania uwierzytelnione, więc selektor autora nadal działa. Usunięcie trasy także dla zalogowanych użytkowników by go zepsuło.

Czy „admin” nadal jest ryzykiem, jeśli mam 2FA?

Znacznie mniejszym. Przy 2FA samo odgadnięte hasło nikogo nie wpuści. Ale login admin przyciąga ciągłe próby zgadywania, które zapełniają logi i mogą wywoływać blokady, więc nadal warto go zastąpić.

Czy powinienem zmienić sam login i jak to zrobić?

WordPress nie ma przycisku do zmiany loginu. Należy utworzyć nowego administratora z neutralnym loginem i 2FA, zalogować się na to konto, a potem usunąć stare konto i przypisać jego treści nowemu, gdy WordPress o to zapyta. Przez cały czas musi istnieć co najmniej jeden działający administrator. Szczegóły znajdą Państwo w artykule bezpieczeństwo ról użytkowników w WordPressie.

Sprawdź swoją stronę

Security Audit & Compliance to wtyczka WordPress od secaudit.eu: wyszukuje złośliwe oprogramowanie, ukryty spam, zmienione pliki rdzenia i podatny kod oraz przypisuje wyniki do art. 32 RODO, art. 21 NIS2 i OWASP Top 10.

Zobacz, co sprawdza wtyczka
Melike Görel

Melike Görel pisze poradniki secaudit.eu o bezpieczeństwie WordPressa i zgodności z przepisami UE dla AngelForce OÜ — firmy programistycznej z Tallinna, która stoi za secaudit.eu.

Przygotowano z pomocą AI i sprawdzono przed publikacją. Ten artykuł ma charakter ogólnoinformacyjny i nie stanowi porady prawnej.