REST API: enumeracja użytkowników
ważność wysokamierzonysposób exposure
Czy /wp-json/wp/v2/users albo ?rest_route= ujawnia listę loginów. Atakujący zna połowę danych do brute-force konta admina.
WIKI
artykuł z 16.09.2026 · źródeł 10REST API: enumeracja użytkowników to parametr monitoringu stron WordPress, który sprawdza, czy anonimowy klient może z zewnątrz poznać identyfikatory kont: z listy użytkowników w REST API albo z przekierowania na archiwum autora. Pomiar kończy się jednym z trzech wyników: identyfikatory ujawnione, nieujawnione albo brak odpowiedzi w wyznaczonym czasie. Trasa /wp/v2/users REST API WordPressa zwraca rekordy użytkowników z polami takimi jak id, name, slug, link i avatar_urls [1].
Jak działa
Pola rekordu użytkownika są przypisane do kontekstów. W domyślnym kontekście view widać m.in. id, name, url, description, link i slug, a login (username), adres e-mail i role należą wyłącznie do kontekstu edit [1]. Kontroler odrzuca kontekst edit, jeśli klient nie ma uprawnienia list_users, a takim klientom zawęża listę do osób z opublikowanymi wpisami w typach treści dostępnych przez REST [4]. Lista ma domyślnie 10 rekordów na stronę [1].
Pole slug to wartość user_nicename [4]. Jeśli przy zakładaniu konta nie podano jej osobno, WordPress bierze pierwsze 50 znaków loginu i przepuszcza je przez sanitize_title [6]. Slug jest więc zwykle loginem zapisanym w postaci nadającej się do adresu, ale może się od niego różnić.
Do tej samej trasy prowadzą dwie ścieżki. Na stronach bez przyjaznych odnośników trasa trafia do parametru rest_route, np. ?rest_route=/wp/v2/posts/123 [2]. Kod rdzenia rejestruje rest_route jako zmienną zapytania, a adresy zaczynające się od /wp-json/ przepisuje regułami na index.php?rest_route= [3]. Zablokowanie samej ścieżki /wp-json/ nie zamyka zatem parametru.
Trzecia droga nie korzysta z REST. Gdy żądanie strony ma liczbowy parametr author, a użytkownik o tym numerze ma choć jeden opublikowany wpis, funkcja kanonicznych przekierowań odsyła klienta na adres archiwum autora zbudowany z user_nicename [5]. Podstawiając kolejne liczby, można w ten sposób odczytać slug każdego autora.
Standardy i specyfikacje
- REST API Handbook, Users definiuje pola rekordu, ich konteksty i parametry listy, w tym
per_pageihas_published_posts[1]. - Core Contributor Handbook, FAQ bezpieczeństwa stwierdza, że projekt WordPress nie uważa nazw użytkowników ani ich numerów za prywatne: login ma identyfikować, a weryfikacja jest zadaniem hasła [7]. Publiczny dostęp do listy przez
GET /wp-json/wp/v2/usersjest według tego dokumentu zamierzony [7]. WordPress pozwala logować się loginem albo adresem e-mail i zamiast ukrywać identyfikator zachęca do silnych haseł [7]. - OWASP WSTG-IDNT-04 każe sprawdzić, czy da się zebrać listę prawidłowych nazw użytkowników, bo przydaje się ona w ataku siłowym albo przy próbach domyślnych haseł [8]. Przewodnik zaznacza, że część aplikacji nie traktuje nazw użytkowników jako wrażliwych i celowo je wyświetla, więc przed zgłoszeniem trzeba znać wymagania aplikacji [8]. Wśród zaleceń wymienia jednolite komunikaty błędów logowania i usuwanie kont domyślnych oraz testowych przed wdrożeniem [8].
Zagrożenia i skutki
Połowa danych logowania. Lista kont pozwala skupić próby haseł na istniejących identyfikatorach zamiast zgadywać także nazwę [8]. Stanowisko WordPressa przenosi ciężar ochrony na hasło [7], więc realne ryzyko zależy od siły haseł i od tego, czy slug pokrywa się z loginem [6].
Pusta lista nie oznacza braku kont. Anonimowy klient widzi tylko autorów opublikowanych wpisów [4], a przekierowanie z ?author= działa tylko dla użytkownika z opublikowanym wpisem [5]. Administrator, który niczego nie publikuje, nie pojawi się żadną z tych dróg.
Blokada jednej ścieżki. Reguła odmawiająca dostępu do /wp-json/ nie obejmuje żądania z parametrem rest_route, bo obie drogi kończą się w tej samej obsłudze zmiennej zapytania [3].
Przykłady
Dokumentacja REST API odradza wyłączanie całego interfejsu, bo przestają wtedy działać funkcje panelu administracyjnego; zamiast tego pozwala wymagać uwierzytelnienia filtrem rest_authentication_errors ze sprawdzeniem is_user_logged_in [10]. Węższym narzędziem jest filtr rest_endpoints (od wersji 4.4.0), który filtruje tablicę dostępnych tras REST API i pozwala usunąć z niej trasy użytkowników [9]. Po stronie archiwum autora przekierowanie przechodzi przez filtr redirect_canonical, a zwrócenie z niego false anuluje przekierowanie [5].
| Żądanie lub ustawienie | Odpowiedź | Co ujawnia |
|---|---|---|
GET /wp-json/wp/v2/users bez logowania [1] [4] | 200 z listą autorów wpisów | id, name, slug, link |
GET /wp-json/wp/v2/users?context=edit bez uprawnień [4] | odmowa | login, e-mail i role pozostają ukryte |
GET /?rest_route=/wp/v2/users przy blokadzie /wp-json/ [2] [3] | 200 z tą samą listą | slug |
GET /?author=1, autor z opublikowanym wpisem [5] | przekierowanie na archiwum autora | slug w adresie |
GET /?author=1, konto bez wpisów [5] | brak przekierowania | nic |
Filtr rest_endpoints usuwający trasy użytkowników [9] | brak trasy | lista niedostępna |
Filtr redirect_canonical zwracający false dla ?author= [5] | brak przekierowania | slug nie trafia do nagłówka Location |
rest_authentication_errors z is_user_logged_in [10] | błąd dla anonimowych | cały REST tylko po zalogowaniu |
- [1] Users — REST API Handbook Reference · WordPress.org
- [2] Routes and Endpoints — REST API Handbook · WordPress.org
- [3] rest-api.php — rejestracja tras i reguł przepisywania REST API · WordPress
- [4] class-wp-rest-users-controller.php — kontroler /wp/v2/users · WordPress
- [5] canonical.php — funkcja redirect_canonical · WordPress
- [6] user.php — funkcja wp_insert_user · WordPress
- [7] Reporting Security Vulnerabilities — Core Contributor Handbook · WordPress.org
- [8] WSTG-IDNT-04 — Account Enumeration and Guessable User Account · OWASP Web Security Testing Guide
- [9] Hook rest_endpoints — Code Reference · WordPress.org
- [10] Frequently Asked Questions — REST API Handbook · WordPress.org
STATS
wyniki z systemu · tylko liczby zbiorczeTen parametr nie zapisuje odczytu per strona — przebieg zostawia tylko ustalenia, więc liczby sprawdzonych stron i wyników „ok” system nie ma. Pojawią się po wdrożeniu zapisu przebiegów per strona per parametr.
- Otwarte teraz
- 10stron: 7
- Nowe w ostatnim dniu
- 809.09.2026
- Otwarte od początku
- 10stron: 7
- Zamknięte
- 0od początku
- Mediana trwania
- —od wykrycia do zamknięcia
| Ustalenie | Skutek | Otwarte teraz | Łącznie | Zamknięte |
|---|---|---|---|---|
| Enumeracja użytkowników exposure_users_enum | PODATNA | 6 | 6 | 0 |
| Login administratora z /?author=1 exposure_author_enum | PODATNA | 4 | 4 | 0 |
| Dzień | nowe ustalenia |
|---|---|
| 23.08.2026 | 0 ustaleń |
| 24.08.2026 | 0 ustaleń |
| 25.08.2026 | 0 ustaleń |
| 26.08.2026 | 0 ustaleń |
| 27.08.2026 | 0 ustaleń |
| 28.08.2026 | 0 ustaleń |
| 29.08.2026 | 0 ustaleń |
| 30.08.2026 | 0 ustaleń |
| 31.08.2026 | 0 ustaleń |
| 01.09.2026 | 0 ustaleń |
| 02.09.2026 | 0 ustaleń |
| 03.09.2026 | 0 ustaleń |
| 04.09.2026 | 0 ustaleń |
| 05.09.2026 | 0 ustaleń |
| 06.09.2026 | 0 ustaleń |
| 07.09.2026 | 0 ustaleń |
| 08.09.2026 | 0 ustaleń |
| 09.09.2026 | 8 ustaleń |
| 10.09.2026 | 0 ustaleń |
| 11.09.2026 | 0 ustaleń |
| 12.09.2026 | 0 ustaleń |
| 13.09.2026 | 0 ustaleń |
| 14.09.2026 | 0 ustaleń |
| 15.09.2026 | 0 ustaleń |
| 16.09.2026 | 0 ustaleń |
| 17.09.2026 | 0 ustaleń |
| 18.09.2026 | 0 ustaleń |
| 19.09.2026 | 0 ustaleń |
| 20.09.2026 | 0 ustaleń |
| 21.09.2026 | 0 ustaleń |
- Przebiegów
- 33sposób exposure
- W 30 dni
- 33przebiegów
- Ostatni
- 19.09.2026start przebiegu
- Stron w przebiegach
- —33 przebiegów bez liczby stron
CARD
karta z kodu CREATO_PING, odczyt 16.09.2026| Obszar i ważność | Bezpieczeństwo · wysoka (B11) |
|---|---|
| Workflow i częstotliwość | creato_ping_security_v2 · P3 działa na LAB247cron co 24 h, T1–T3; T4 raz po anomalii · Dokładny skan. dns_integrity porównuje NS, MX, SPF i DMARC z wzorcem z poprzedniego przebiegu. |
| Sonda i helper | sposób exposure · CREATO_PING/helpers/exposure_check.py |
| Testy |
|
| Typ wyniku | 0 do 2 ustaleń: exposure_users_enum (REST) i exposure_author_enum (przekierowanie autora) |
| Jednostka | brak — wynik jakościowy; w dowodzie liczba i pierwsze trzy slugi kont albo slug z przekierowania |
| Próg i reguła | Timeout odpowiedzi 15 s, połączenia 5 s. 401, 403, 404, pusta tablica, błąd sieci i timeout = brak ustalenia. |
| Gdzie leżą pokrętła | CREATO_PING/helpers/exposure_check.py → PROBES (json_users, author_redirect), _AUTHOR_LOCATION, _REDIRECT_CODESCREATO_PING/helpers/config.py → TIMEOUTS["exposure"], TIMEOUTS["security_connect"]CREATO_PING/helpers/security_runner.py → _TASK_SEVERITIESCREATO_PING/config/incident_policy.toml → [finding] exposure_users_enum, exposure_author_enum |
| Retencja odczytów | Odczytu per strona nie zapisujemy — zostaje tylko ustalenie w tabeli findings (otwarcie, zamknięcie, dowód). Replay po surowych odczytach dziś niemożliwy. |
| Znane rozbieżności |
|
| Karta przepisana z | CREATO_PING/helpers/exposure_check.py · CREATO_PING/helpers/config.py · CREATO_PING/config/incident_policy.toml · CREATO_PING/helpers/security_runner.py · CREATO_PING/helpers/clickup_writer_v2.py · odczyt 16.09.2026 |
Oba typy: powaga wysoka, pewność ewidentna, skutek PODATNA — do dwóch osobnych subtasków z priorytetem wysokim, status strony przeliczony na PODATNA.
| Ustalenie | Powaga | Pewność | Skutek | Subtask / alarm | Co zrobić |
|---|---|---|---|---|---|
| Enumeracja użytkownikówexposure_users_enum | Wysokie | Ewidentne | PODATNA | tak / nie | Zablokuj /wp-json/wp/v2/users i ?author=N. |
| Login administratora z /?author=1exposure_author_enum | Wysokie | Ewidentne | PODATNA | tak / nie | Zablokuj przekierowanie ?author=N (reguła serwera albo wtyczka bezpieczeństwa). |
LOOP
pętli: 3Trafności jeszcze nie liczymy: zamknięcie subtaska nie niesie dziś werdyktu (prawdziwy / fałszywy / nie dało się sprawdzić). Cztery liczby pojawią się, gdy każde zamknięcie dostanie werdykt — to warunek startu pętli 1.
Sygnał: Tarcze antybotowe hostingu, strony parkingowe i serwery catch-all odpowiadają 200 na każdą ścieżkę, więc sondy oparte na kodzie 200 dawały „wystawiony” wszędzie. Równocześnie sondy na wolnych hostingach odpowiadały po około 15 s, a limit 5 s ucinał je bez śladu. · dotyczy też: Ekspozycja .env, Ekspozycja plików wrażliwych, Ekspozycja .htaccess.bak, Dostępność xmlrpc.php, Ekspozycja .git/HEAD, Dostępność readme.html, Dostępność wp-cron.php, Ekspozycja kopii zapasowych, Ekspozycja zrzutu bazy, Ekspozycja manifestów dev, Ekspozycja .DS_Store
- 1. Zbiórkaręcznieodczyt z nocnego skanu, bez zapisu per strona
- 2. Werdyktręcznieprzegląd skanera: 200 na wszystko i ciche timeouty to fałszywe wyniki
- 3. Miarabrakliczników trafności wtedy nie było
- 4. Kartabrakkarta parametru powstała 2026-09-16
- 5. Zmianazrobionestrażnik ścieżki, 15 s, logowanie błędów
- 6. Replaybrakbrak zapisanych odczytów do replayu
| Data | Zmiana | Powód | Wpływ na system | Commit · replay |
|---|---|---|---|---|
| 2026-09-05 | Przed sondami GET losowej, nieistniejącej ścieżki; odpowiedź 200 z treścią = zero sond i jedno ustalenie exposure_scan_unreliable (niska powaga, bez subtaska).helpers/exposure_check.py · config/incident_policy.toml | Odpowiedź 200 na losową ścieżkę dowodzi, że kod 200 na tym serwerze nic nie znaczy; dalsze sondowanie tarczy grozi banem IP monitoringu. | cały system Obejmuje wszystkie sondy ekspozycji naraz i wprowadza typ „nie dało się sprawdzić”; tę samą konwencję dostało wykrywanie wtyczek. Ustalenie ma niską powagę, więc filtr przebiegu je odrzuca i w produkcji nie zostawia śladu. | b0fc77cbez replayubez zapisu zatwierdzenia |
| 2026-09-05 | Limit odpowiedzi z 5 s na 15 s i połączenia z 2 s na 5 s, przeniesione do config.py; błąd sieci sondy logowany zamiast cichego pustego wyniku; do 4 sond naraz na stronę.helpers/config.py · helpers/exposure_check.py · helpers/html_fetch.py · helpers/wp_fingerprint.py | Wolne hostingi odpowiadały po około 15 s, a pusty wynik po timeoucie był nieodróżnialny od „sprawdzone, czysto”. | cały system Limit połączenia 5 s przejęło wspólne pobieranie HTML całego skanu bezpieczeństwa i wykrywanie wersji — strony z wolnym TLS przestały wypadać z całego skanu. | b0fc77cbez replayubez zapisu zatwierdzenia |
Sygnał: Brak pamięci podczas skanu: publiczny debug.log jednej ze stron był bardzo duży, a klient HTTP buforował całą odpowiedź. · dotyczy też: Sitemap.xml, Ekspozycja plików wrażliwych, Ekspozycja .env, Ekspozycja .htaccess.bak, Dostępność xmlrpc.php, Ekspozycja .git/HEAD, Dostępność readme.html, Dostępność wp-cron.php, Ekspozycja kopii zapasowych, Ekspozycja zrzutu bazy, Ekspozycja manifestów dev, Ekspozycja .DS_Store
- 1. Zbiórkaręcznieodczyt z nocnego skanu, bez zapisu per strona
- 2. Werdyktręcznieawaria procesu przypisana ścieżce debug.log
- 3. Miarabrakliczników trafności wtedy nie było
- 4. Kartabrakkarta parametru powstała 2026-09-16
- 5. Zmianazrobionestrumień z limitem 64 KB
- 6. Replaybrakbrak zapisanych odczytów do replayu
| Data | Zmiana | Powód | Wpływ na system | Commit · replay |
|---|---|---|---|---|
| 2026-07-19 | Odpowiedź sondy czytana strumieniem do 64 KB; do oceny idzie pierwsze 5000 znaków.helpers/exposure_check.py · helpers/html_fetch.py · helpers/sitemap_check.py | Do rozpoznania ekspozycji wystarcza początek pliku; zmiana dotyczy sposobu czytania, nie werdyktu. | cały system Brak pamięci w helperze zatrzymywał cały proces skanu bezpieczeństwa partii stron; ten sam limit dostało sprawdzenie sitemap. | 343b699bez replayubez zapisu zatwierdzenia |
Sygnał: Blokada /wp-json/ na poziomie WAF-a dawała „czysto”, choć ta sama lista kont była dostępna przez /?rest_route=/wp/v2/users, a login administratora zdradzało przekierowanie /?author=1.
- 1. Zbiórkaręcznieodczyt z nocnego skanu, bez zapisu per strona
- 2. Werdyktręcznieprzegląd skanera: blokada jednej ścieżki nie zamyka enumeracji
- 3. Miarabrakliczników trafności wtedy nie było
- 4. Kartabrakkarta parametru powstała 2026-09-16
- 5. Zmianazrobionedwie dodatkowe drogi enumeracji
- 6. Replaybrakbrak zapisanych odczytów do replayu
| Data | Zmiana | Powód | Wpływ na system | Commit · replay |
|---|---|---|---|---|
| 2026-09-05 | Sonda /?rest_route=/wp/v2/users obok /wp-json/wp/v2/users; trafienia sklejane w jedno ustalenie, w dowodzie liczba kont i trzy pierwsze loginy.helpers/exposure_check.py | Parametr rest_route omija blokadę samej ścieżki /wp-json/. | tylko ten parametr | b0fc77cbez replayubez zapisu zatwierdzenia |
| 2026-09-05 | Sonda /?author=1 bez podążania za przekierowaniem: 3xx na /author/<login>/ = ustalenie exposure_author_enum (wysoka powaga, subtask PODATNA).helpers/exposure_check.py · config/incident_policy.toml | Login administratora wycieka przekierowaniem nawet wtedy, gdy REST jest zablokowany. | tylko ten parametr | b0fc77cbez replayubez zapisu zatwierdzenia |